@pyscript/core 0.4.41 → 0.4.43

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.
Files changed (48) hide show
  1. package/dist/{codemirror-zS6ccqby.js → codemirror-Dl5H49bb.js} +2 -2
  2. package/dist/{codemirror-zS6ccqby.js.map → codemirror-Dl5H49bb.js.map} +1 -1
  3. package/dist/{codemirror_commands-4671n0xX.js → codemirror_commands-BdjCAcK-.js} +2 -2
  4. package/dist/{codemirror_commands-4671n0xX.js.map → codemirror_commands-BdjCAcK-.js.map} +1 -1
  5. package/dist/{codemirror_lang-python-D74-Kgzp.js → codemirror_lang-python--SQq2qB4.js} +2 -2
  6. package/dist/{codemirror_lang-python-D74-Kgzp.js.map → codemirror_lang-python--SQq2qB4.js.map} +1 -1
  7. package/dist/{codemirror_language-BheMcNfw.js → codemirror_language-CAKlJ8mf.js} +2 -2
  8. package/dist/{codemirror_language-BheMcNfw.js.map → codemirror_language-CAKlJ8mf.js.map} +1 -1
  9. package/dist/codemirror_view-BQKcFXby.js +2 -0
  10. package/dist/codemirror_view-BQKcFXby.js.map +1 -0
  11. package/dist/core-B6hsMz8Y.js +3 -0
  12. package/dist/{core-Bn-wWzQh.js.map → core-B6hsMz8Y.js.map} +1 -1
  13. package/dist/core.js +1 -1
  14. package/dist/{deprecations-manager-CoryDgC-.js → deprecations-manager-De5K5cXk.js} +2 -2
  15. package/dist/{deprecations-manager-CoryDgC-.js.map → deprecations-manager-De5K5cXk.js.map} +1 -1
  16. package/dist/{error-DCbG1SNB.js → error-Dfrs7Q5J.js} +2 -2
  17. package/dist/{error-DCbG1SNB.js.map → error-Dfrs7Q5J.js.map} +1 -1
  18. package/dist/{index-O4achdbT.js → index-DUXwuZn0.js} +2 -2
  19. package/dist/{index-O4achdbT.js.map → index-DUXwuZn0.js.map} +1 -1
  20. package/dist/{mpy-B1mmjrIH.js → mpy-Noch7TCk.js} +2 -2
  21. package/dist/{mpy-B1mmjrIH.js.map → mpy-Noch7TCk.js.map} +1 -1
  22. package/dist/{py-CUqUSajL.js → py-6RmoksBq.js} +2 -2
  23. package/dist/{py-CUqUSajL.js.map → py-6RmoksBq.js.map} +1 -1
  24. package/dist/py-editor-DeavJRm_.js +2 -0
  25. package/dist/py-editor-DeavJRm_.js.map +1 -0
  26. package/dist/{py-terminal-B1kgdSnK.js → py-terminal-CVai3EuH.js} +2 -2
  27. package/dist/{py-terminal-B1kgdSnK.js.map → py-terminal-CVai3EuH.js.map} +1 -1
  28. package/package.json +6 -6
  29. package/src/core.js +1 -0
  30. package/src/plugins/py-editor.js +42 -12
  31. package/src/stdlib/pyscript/__init__.py +2 -0
  32. package/src/stdlib/pyscript/event_handling.py +9 -7
  33. package/src/stdlib/pyscript/magic_js.py +5 -1
  34. package/src/stdlib/pyscript/web/__init__.py +5 -0
  35. package/src/stdlib/pyscript/web/dom.py +21 -0
  36. package/src/stdlib/pyscript/web/elements.py +1484 -0
  37. package/src/stdlib/pyscript.js +9 -8
  38. package/src/stdlib.js +1 -1
  39. package/types/stdlib/pyscript.d.ts +6 -5
  40. package/dist/codemirror_view-C3_bb6sY.js +0 -2
  41. package/dist/codemirror_view-C3_bb6sY.js.map +0 -1
  42. package/dist/core-Bn-wWzQh.js +0 -3
  43. package/dist/py-editor-CqRyqS4n.js +0 -2
  44. package/dist/py-editor-CqRyqS4n.js.map +0 -1
  45. package/dist.zip +0 -0
  46. package/src/stdlib/pyweb/__init__.py +0 -1
  47. package/src/stdlib/pyweb/pydom.py +0 -550
  48. /package/src/stdlib/{pyweb → pyscript/web}/media.py +0 -0
@@ -1,550 +0,0 @@
1
- try:
2
- from typing import Any
3
- except ImportError:
4
- Any = "Any"
5
-
6
- try:
7
- import warnings
8
- except ImportError:
9
- # TODO: For now it probably means we are in MicroPython. We should figure
10
- # out the "right" way to handle this. For now we just ignore the warning
11
- # and logging to console
12
- class warnings:
13
- @staticmethod
14
- def warn(*args, **kwargs):
15
- print("WARNING: ", *args, **kwargs)
16
-
17
-
18
- try:
19
- from functools import cached_property
20
- except ImportError:
21
- # TODO: same comment about micropython as above
22
- cached_property = property
23
-
24
- try:
25
- from pyodide.ffi import JsProxy
26
- except ImportError:
27
- # TODO: same comment about micropython as above
28
- def JsProxy(obj):
29
- return obj
30
-
31
-
32
- from pyscript import display, document, window
33
-
34
- alert = window.alert
35
-
36
-
37
- class BaseElement:
38
- def __init__(self, js_element):
39
- self._js = js_element
40
- self._parent = None
41
- self.style = StyleProxy(self)
42
- self._proxies = {}
43
-
44
- def __eq__(self, obj):
45
- """Check if the element is the same as the other element by comparing
46
- the underlying JS element"""
47
- return isinstance(obj, BaseElement) and obj._js == self._js
48
-
49
- @property
50
- def parent(self):
51
- if self._parent:
52
- return self._parent
53
-
54
- if self._js.parentElement:
55
- self._parent = self.__class__(self._js.parentElement)
56
-
57
- return self._parent
58
-
59
- @property
60
- def __class(self):
61
- return self.__class__ if self.__class__ != PyDom else Element
62
-
63
- def create(self, type_, is_child=True, classes=None, html=None, label=None):
64
- js_el = document.createElement(type_)
65
- element = self.__class(js_el)
66
-
67
- if classes:
68
- for class_ in classes:
69
- element.add_class(class_)
70
-
71
- if html is not None:
72
- element.html = html
73
-
74
- if label is not None:
75
- element.label = label
76
-
77
- if is_child:
78
- self.append(element)
79
-
80
- return element
81
-
82
- def find(self, selector):
83
- """Return an ElementCollection representing all the child elements that
84
- match the specified selector.
85
-
86
- Args:
87
- selector (str): A string containing a selector expression
88
-
89
- Returns:
90
- ElementCollection: A collection of elements matching the selector
91
- """
92
- elements = self._js.querySelectorAll(selector)
93
- if not elements:
94
- return None
95
- return ElementCollection([Element(el) for el in elements])
96
-
97
-
98
- class Element(BaseElement):
99
- @property
100
- def children(self):
101
- return [self.__class__(el) for el in self._js.children]
102
-
103
- def append(self, child):
104
- # TODO: this is Pyodide specific for now!!!!!!
105
- # if we get passed a JSProxy Element directly we just map it to the
106
- # higher level Python element
107
- if isinstance(child, JsProxy):
108
- return self.append(Element(child))
109
-
110
- elif isinstance(child, Element):
111
- self._js.appendChild(child._js)
112
-
113
- return child
114
-
115
- elif isinstance(child, ElementCollection):
116
- for el in child:
117
- self.append(el)
118
-
119
- # -------- Pythonic Interface to Element -------- #
120
- @property
121
- def html(self):
122
- return self._js.innerHTML
123
-
124
- @html.setter
125
- def html(self, value):
126
- self._js.innerHTML = value
127
-
128
- @property
129
- def text(self):
130
- return self._js.textContent
131
-
132
- @text.setter
133
- def text(self, value):
134
- self._js.textContent = value
135
-
136
- @property
137
- def content(self):
138
- # TODO: This breaks with with standard template elements. Define how to best
139
- # handle this specifica use case. Just not support for now?
140
- if self._js.tagName == "TEMPLATE":
141
- warnings.warn(
142
- "Content attribute not supported for template elements.", stacklevel=2
143
- )
144
- return None
145
- return self._js.innerHTML
146
-
147
- @content.setter
148
- def content(self, value):
149
- # TODO: (same comment as above)
150
- if self._js.tagName == "TEMPLATE":
151
- warnings.warn(
152
- "Content attribute not supported for template elements.", stacklevel=2
153
- )
154
- return
155
-
156
- display(value, target=self.id)
157
-
158
- @property
159
- def id(self):
160
- return self._js.id
161
-
162
- @id.setter
163
- def id(self, value):
164
- self._js.id = value
165
-
166
- @property
167
- def options(self):
168
- if "options" in self._proxies:
169
- return self._proxies["options"]
170
-
171
- if not self._js.tagName.lower() in {"select", "datalist", "optgroup"}:
172
- raise AttributeError(
173
- f"Element {self._js.tagName} has no options attribute."
174
- )
175
- self._proxies["options"] = OptionsProxy(self)
176
- return self._proxies["options"]
177
-
178
- @property
179
- def value(self):
180
- return self._js.value
181
-
182
- @value.setter
183
- def value(self, value):
184
- # in order to avoid confusion to the user, we don't allow setting the
185
- # value of elements that don't have a value attribute
186
- if not hasattr(self._js, "value"):
187
- raise AttributeError(
188
- f"Element {self._js.tagName} has no value attribute. If you want to "
189
- "force a value attribute, set it directly using the `_js.value = <value>` "
190
- "javascript API attribute instead."
191
- )
192
- self._js.value = value
193
-
194
- @property
195
- def selected(self):
196
- return self._js.selected
197
-
198
- @selected.setter
199
- def selected(self, value):
200
- # in order to avoid confusion to the user, we don't allow setting the
201
- # value of elements that don't have a value attribute
202
- if not hasattr(self._js, "selected"):
203
- raise AttributeError(
204
- f"Element {self._js.tagName} has no value attribute. If you want to "
205
- "force a value attribute, set it directly using the `_js.value = <value>` "
206
- "javascript API attribute instead."
207
- )
208
- self._js.selected = value
209
-
210
- def clone(self, new_id=None):
211
- clone = Element(self._js.cloneNode(True))
212
- clone.id = new_id
213
-
214
- return clone
215
-
216
- def remove_class(self, classname):
217
- classList = self._js.classList
218
- if isinstance(classname, list):
219
- classList.remove(*classname)
220
- else:
221
- classList.remove(classname)
222
- return self
223
-
224
- def add_class(self, classname):
225
- classList = self._js.classList
226
- if isinstance(classname, list):
227
- classList.add(*classname)
228
- else:
229
- self._js.classList.add(classname)
230
- return self
231
-
232
- @property
233
- def classes(self):
234
- classes = self._js.classList.values()
235
- return [x for x in classes]
236
-
237
- def show_me(self):
238
- self._js.scrollIntoView()
239
-
240
- def snap(
241
- self,
242
- to: BaseElement | str = None,
243
- width: int | None = None,
244
- height: int | None = None,
245
- ):
246
- """
247
- Captures a snapshot of a video element. (Only available for video elements)
248
-
249
- Inputs:
250
-
251
- * to: element where to save the snapshot of the video frame to
252
- * width: width of the image
253
- * height: height of the image
254
-
255
- Output:
256
- (Element) canvas element where the video frame snapshot was drawn into
257
- """
258
- if self._js.tagName != "VIDEO":
259
- raise AttributeError("Snap method is only available for video Elements")
260
-
261
- if to is None:
262
- canvas = self.create("canvas")
263
- if width is None:
264
- width = self._js.width
265
- if height is None:
266
- height = self._js.height
267
- canvas._js.width = width
268
- canvas._js.height = height
269
-
270
- elif isinstance(to, Element):
271
- if to._js.tagName != "CANVAS":
272
- raise TypeError("Element to snap to must a canvas.")
273
- canvas = to
274
- elif getattr(to, "tagName", "") == "CANVAS":
275
- canvas = Element(to)
276
- elif isinstance(to, str):
277
- canvas = pydom[to][0]
278
- if canvas._js.tagName != "CANVAS":
279
- raise TypeError("Element to snap to must a be canvas.")
280
-
281
- canvas.draw(self, width, height)
282
-
283
- return canvas
284
-
285
- def download(self, filename: str = "snapped.png") -> None:
286
- """Download the current element (only available for canvas elements) with the filename
287
- provided in input.
288
-
289
- Inputs:
290
- * filename (str): name of the file being downloaded
291
-
292
- Output:
293
- None
294
- """
295
- if self._js.tagName != "CANVAS":
296
- raise AttributeError(
297
- "The download method is only available for canvas Elements"
298
- )
299
-
300
- link = self.create("a")
301
- link._js.download = filename
302
- link._js.href = self._js.toDataURL()
303
- link._js.click()
304
-
305
- def draw(self, what, width, height):
306
- """Draw `what` on the current element (only available for canvas elements).
307
-
308
- Inputs:
309
-
310
- * what (canvas image source): An element to draw into the context. The specification permits any canvas
311
- image source, specifically, an HTMLImageElement, an SVGImageElement, an HTMLVideoElement,
312
- an HTMLCanvasElement, an ImageBitmap, an OffscreenCanvas, or a VideoFrame.
313
- """
314
- if self._js.tagName != "CANVAS":
315
- raise AttributeError(
316
- "The draw method is only available for canvas Elements"
317
- )
318
-
319
- if isinstance(what, Element):
320
- what = what._js
321
-
322
- # https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
323
- self._js.getContext("2d").drawImage(what, 0, 0, width, height)
324
-
325
-
326
- class OptionsProxy:
327
- """This class represents the options of a select element. It
328
- allows to access to add and remove options by using the `add` and `remove` methods.
329
- """
330
-
331
- def __init__(self, element: Element) -> None:
332
- self._element = element
333
- if self._element._js.tagName.lower() != "select":
334
- raise AttributeError(
335
- f"Element {self._element._js.tagName} has no options attribute."
336
- )
337
-
338
- def add(
339
- self,
340
- value: Any = None,
341
- html: str = None,
342
- text: str = None,
343
- before: Element | int = None,
344
- **kws,
345
- ) -> None:
346
- """Add a new option to the select element"""
347
- # create the option element and set the attributes
348
- option = document.createElement("option")
349
- if value is not None:
350
- kws["value"] = value
351
- if html is not None:
352
- option.innerHTML = html
353
- if text is not None:
354
- kws["text"] = text
355
-
356
- for key, value in kws.items():
357
- option.setAttribute(key, value)
358
-
359
- if before:
360
- if isinstance(before, Element):
361
- before = before._js
362
-
363
- self._element._js.add(option, before)
364
-
365
- def remove(self, item: int) -> None:
366
- """Remove the option at the specified index"""
367
- self._element._js.remove(item)
368
-
369
- def clear(self) -> None:
370
- """Remove all the options"""
371
- for i in range(len(self)):
372
- self.remove(0)
373
-
374
- @property
375
- def options(self):
376
- """Return the list of options"""
377
- return [Element(opt) for opt in self._element._js.options]
378
-
379
- @property
380
- def selected(self):
381
- """Return the selected option"""
382
- return self.options[self._element._js.selectedIndex]
383
-
384
- def __iter__(self):
385
- yield from self.options
386
-
387
- def __len__(self):
388
- return len(self.options)
389
-
390
- def __repr__(self):
391
- return f"{self.__class__.__name__} (length: {len(self)}) {self.options}"
392
-
393
- def __getitem__(self, key):
394
- return self.options[key]
395
-
396
-
397
- class StyleProxy: # (dict):
398
- def __init__(self, element: Element) -> None:
399
- self._element = element
400
-
401
- @cached_property
402
- def _style(self):
403
- return self._element._js.style
404
-
405
- def __getitem__(self, key):
406
- return self._style.getPropertyValue(key)
407
-
408
- def __setitem__(self, key, value):
409
- self._style.setProperty(key, value)
410
-
411
- def remove(self, key):
412
- self._style.removeProperty(key)
413
-
414
- def set(self, **kws):
415
- for k, v in kws.items():
416
- self._element._js.style.setProperty(k, v)
417
-
418
- # CSS Properties
419
- # Reference: https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts#L3799C1-L5005C2
420
- # Following prperties automatically generated from the above reference using
421
- # tools/codegen_css_proxy.py
422
- @property
423
- def visible(self):
424
- return self._element._js.style.visibility
425
-
426
- @visible.setter
427
- def visible(self, value):
428
- self._element._js.style.visibility = value
429
-
430
-
431
- class StyleCollection:
432
- def __init__(self, collection: "ElementCollection") -> None:
433
- self._collection = collection
434
-
435
- def __get__(self, obj, objtype=None):
436
- return obj._get_attribute("style")
437
-
438
- def __getitem__(self, key):
439
- return self._collection._get_attribute("style")[key]
440
-
441
- def __setitem__(self, key, value):
442
- for element in self._collection._elements:
443
- element.style[key] = value
444
-
445
- def remove(self, key):
446
- for element in self._collection._elements:
447
- element.style.remove(key)
448
-
449
-
450
- class ElementCollection:
451
- def __init__(self, elements: [Element]) -> None:
452
- self._elements = elements
453
- self.style = StyleCollection(self)
454
-
455
- def __getitem__(self, key):
456
- # If it's an integer we use it to access the elements in the collection
457
- if isinstance(key, int):
458
- return self._elements[key]
459
- # If it's a slice we use it to support slice operations over the elements
460
- # in the collection
461
- elif isinstance(key, slice):
462
- return ElementCollection(self._elements[key])
463
-
464
- # If it's anything else (basically a string) we use it as a selector
465
- # TODO: Write tests!
466
- elements = self._element.querySelectorAll(key)
467
- return ElementCollection([Element(el) for el in elements])
468
-
469
- def __len__(self):
470
- return len(self._elements)
471
-
472
- def __eq__(self, obj):
473
- """Check if the element is the same as the other element by comparing
474
- the underlying JS element"""
475
- return isinstance(obj, ElementCollection) and obj._elements == self._elements
476
-
477
- def _get_attribute(self, attr, index=None):
478
- if index is None:
479
- return [getattr(el, attr) for el in self._elements]
480
-
481
- # As JQuery, when getting an attr, only return it for the first element
482
- return getattr(self._elements[index], attr)
483
-
484
- def _set_attribute(self, attr, value):
485
- for el in self._elements:
486
- setattr(el, attr, value)
487
-
488
- @property
489
- def html(self):
490
- return self._get_attribute("html")
491
-
492
- @html.setter
493
- def html(self, value):
494
- self._set_attribute("html", value)
495
-
496
- @property
497
- def value(self):
498
- return self._get_attribute("value")
499
-
500
- @value.setter
501
- def value(self, value):
502
- self._set_attribute("value", value)
503
-
504
- @property
505
- def children(self):
506
- return self._elements
507
-
508
- def __iter__(self):
509
- yield from self._elements
510
-
511
- def __repr__(self):
512
- return f"{self.__class__.__name__} (length: {len(self._elements)}) {self._elements}"
513
-
514
-
515
- class DomScope:
516
- def __getattr__(self, __name: str):
517
- element = document[f"#{__name}"]
518
- if element:
519
- return element[0]
520
-
521
-
522
- class PyDom(BaseElement):
523
- # Add objects we want to expose to the DOM namespace since this class instance is being
524
- # remapped as "the module" itself
525
- BaseElement = BaseElement
526
- Element = Element
527
- ElementCollection = ElementCollection
528
-
529
- def __init__(self):
530
- # PyDom is a special case of BaseElement where we don't want to create a new JS element
531
- # and it really doesn't have a need for styleproxy or parent to to call to __init__
532
- # (which actually fails in MP for some reason)
533
- self._js = document
534
- self._parent = None
535
- self._proxies = {}
536
- self.ids = DomScope()
537
- self.body = Element(document.body)
538
- self.head = Element(document.head)
539
-
540
- def create(self, type_, classes=None, html=None):
541
- return super().create(type_, is_child=False, classes=classes, html=html)
542
-
543
- def __getitem__(self, key):
544
- elements = self._js.querySelectorAll(key)
545
- if not elements:
546
- return None
547
- return ElementCollection([Element(el) for el in elements])
548
-
549
-
550
- dom = PyDom()
File without changes