@pyscript/core 0.1.22 → 0.2.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.
Files changed (41) hide show
  1. package/dist/core.css +1 -1
  2. package/dist/core.js +2 -2
  3. package/dist/core.js.map +1 -1
  4. package/dist/error-e4fe78fd.js +2 -0
  5. package/dist/error-e4fe78fd.js.map +1 -0
  6. package/package.json +2 -2
  7. package/src/core.css +3 -1
  8. package/src/core.js +170 -161
  9. package/src/plugins/error.js +2 -2
  10. package/src/stdlib/pyscript/__init__.py +10 -1
  11. package/src/stdlib/pyscript/display.py +7 -0
  12. package/src/stdlib/pyscript/util.py +3 -4
  13. package/src/stdlib/pyscript.js +3 -3
  14. package/dist/error-87e0706c.js +0 -2
  15. package/dist/error-87e0706c.js.map +0 -1
  16. package/tests/integration/__init__.py +0 -0
  17. package/tests/integration/conftest.py +0 -184
  18. package/tests/integration/support.py +0 -1038
  19. package/tests/integration/test_00_support.py +0 -495
  20. package/tests/integration/test_01_basic.py +0 -353
  21. package/tests/integration/test_02_display.py +0 -452
  22. package/tests/integration/test_03_element.py +0 -303
  23. package/tests/integration/test_assets/line_plot.png +0 -0
  24. package/tests/integration/test_assets/tripcolor.png +0 -0
  25. package/tests/integration/test_async.py +0 -197
  26. package/tests/integration/test_event_handling.py +0 -193
  27. package/tests/integration/test_importmap.py +0 -66
  28. package/tests/integration/test_interpreter.py +0 -98
  29. package/tests/integration/test_plugins.py +0 -419
  30. package/tests/integration/test_py_config.py +0 -294
  31. package/tests/integration/test_py_repl.py +0 -663
  32. package/tests/integration/test_py_terminal.py +0 -270
  33. package/tests/integration/test_runtime_attributes.py +0 -64
  34. package/tests/integration/test_script_type.py +0 -121
  35. package/tests/integration/test_shadow_root.py +0 -33
  36. package/tests/integration/test_splashscreen.py +0 -124
  37. package/tests/integration/test_stdio_handling.py +0 -370
  38. package/tests/integration/test_style.py +0 -47
  39. package/tests/integration/test_warnings_and_banners.py +0 -32
  40. package/tests/integration/test_zz_examples.py +0 -419
  41. package/tests/integration/test_zzz_docs_snippets.py +0 -305
@@ -1,452 +0,0 @@
1
- import base64
2
- import io
3
- import os
4
- import re
5
-
6
- import numpy as np
7
- import pytest
8
- from PIL import Image
9
-
10
- from .support import (
11
- PyScriptTest,
12
- filter_inner_text,
13
- filter_page_content,
14
- wait_for_render,
15
- )
16
-
17
- DISPLAY_OUTPUT_ID_PATTERN = r'[id^="py-"]'
18
-
19
-
20
- class TestDisplay(PyScriptTest):
21
- def test_simple_display(self):
22
- self.pyscript_run(
23
- """
24
- <py-script>
25
- print('ciao')
26
- from pyscript import display
27
- display("hello world")
28
- </py-script>
29
- """,
30
- timeout=20000,
31
- )
32
- node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
33
- pattern = r"<div>hello world</div>"
34
- assert node_list[0].inner_html() == pattern
35
- assert len(node_list) == 1
36
-
37
- def test_consecutive_display(self):
38
- self.pyscript_run(
39
- """
40
- <py-script>
41
- from pyscript import display
42
- display('hello 1')
43
- </py-script>
44
- <p>hello 2</p>
45
- <py-script>
46
- from pyscript import display
47
- display('hello 3')
48
- </py-script>
49
- """
50
- )
51
- inner_text = self.page.inner_text("body")
52
- lines = inner_text.splitlines()
53
-
54
- lines = [line for line in filter_page_content(lines)] # remove empty lines
55
- assert lines == ["hello 1", "hello 2", "hello 3"]
56
-
57
- def test_target_attribute(self):
58
- self.pyscript_run(
59
- """
60
- <py-script>
61
- from pyscript import display
62
- display('hello world', target="mydiv")
63
- </py-script>
64
- <div id="mydiv"></div>
65
- """
66
- )
67
- mydiv = self.page.locator("#mydiv")
68
- assert mydiv.inner_text() == "hello world"
69
-
70
- def test_consecutive_display_target(self):
71
- self.pyscript_run(
72
- """
73
- <py-script id="first">
74
- from pyscript import display
75
- display('hello 1')
76
- </py-script>
77
- <p>hello in between 1 and 2</p>
78
- <py-script id="second">
79
- from pyscript import display
80
- display('hello 2', target="second")
81
- </py-script>
82
- <py-script id="third">
83
- from pyscript import display
84
- display('hello 3')
85
- </py-script>
86
- """
87
- )
88
- inner_text = self.page.inner_text("body")
89
- lines = inner_text.splitlines()
90
- lines = [line for line in filter_page_content(lines)] # remove empty lines
91
- assert lines == ["hello 1", "hello in between 1 and 2", "hello 2", "hello 3"]
92
-
93
- def test_multiple_display_calls_same_tag(self):
94
- self.pyscript_run(
95
- """
96
- <py-script>
97
- from pyscript import display
98
- display('hello')
99
- display('world')
100
- </py-script>
101
- """
102
- )
103
- tag = self.page.locator("py-script")
104
- lines = tag.inner_text().splitlines()
105
- assert lines == ["hello", "world"]
106
-
107
- def test_implicit_target_from_a_different_tag(self):
108
- self.pyscript_run(
109
- """
110
- <py-script id="py1">
111
- from pyscript import display
112
- def say_hello():
113
- display('hello')
114
- </py-script>
115
-
116
- <py-script id="py2">
117
- from pyscript import display
118
- say_hello()
119
- </py-script>
120
- """
121
- )
122
- py1 = self.page.locator("#py1")
123
- py2 = self.page.locator("#py2")
124
- assert py1.inner_text() == ""
125
- assert py2.inner_text() == "hello"
126
-
127
- def test_no_explicit_target(self):
128
- self.pyscript_run(
129
- """
130
- <py-script>
131
- from pyscript import display
132
- def display_hello(error):
133
- display('hello world')
134
- </py-script>
135
- <button id="my-button" py-click="display_hello">Click me</button>
136
- """
137
- )
138
- self.page.locator("button").click()
139
-
140
- text = self.page.locator("py-script").text_content()
141
- assert "hello world" in text
142
-
143
- def test_explicit_target_pyscript_tag(self):
144
- self.pyscript_run(
145
- """
146
- <py-script>
147
- from pyscript import display
148
- def display_hello():
149
- display('hello', target='second-pyscript-tag')
150
- </py-script>
151
- <py-script id="second-pyscript-tag">
152
- display_hello()
153
- </py-script>
154
- """
155
- )
156
- text = self.page.locator("id=second-pyscript-tag").inner_text()
157
- assert text == "hello"
158
-
159
- def test_explicit_target_on_button_tag(self):
160
- self.pyscript_run(
161
- """
162
- <py-script>
163
- from pyscript import display
164
- def display_hello(error):
165
- display('hello', target='my-button')
166
- </py-script>
167
- <button id="my-button" py-click="display_hello">Click me</button>
168
- """
169
- )
170
- self.page.locator("text=Click me").click()
171
- text = self.page.locator("id=my-button").inner_text()
172
- assert "hello" in text
173
-
174
- def test_explicit_different_target_from_call(self):
175
- self.pyscript_run(
176
- """
177
- <py-script id="first-pyscript-tag">
178
- from pyscript import display
179
- def display_hello():
180
- display('hello', target='second-pyscript-tag')
181
- </py-script>
182
- <py-script id="second-pyscript-tag">
183
- print('nothing to see here')
184
- </py-script>
185
- <py-script>
186
- display_hello()
187
- </py-script>
188
- """
189
- )
190
- text = self.page.locator("id=second-pyscript-tag").all_inner_texts()
191
- assert "hello" in text
192
-
193
- def test_append_true(self):
194
- self.pyscript_run(
195
- """
196
- <py-script>
197
- from pyscript import display
198
- display('hello world', append=True)
199
- </py-script>
200
- """
201
- )
202
- node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
203
- pattern = r"<div>hello world</div>"
204
-
205
- assert node_list[0].inner_html() == pattern
206
- assert len(node_list) == 1
207
-
208
- def test_append_false(self):
209
- self.pyscript_run(
210
- """
211
- <py-script>
212
- from pyscript import display
213
- display('hello world', append=False)
214
- </py-script>
215
- """
216
- )
217
- inner_html = self.page.content()
218
- pattern = r'<py-script id="py-.*">hello world</py-script>'
219
- assert re.search(pattern, inner_html)
220
-
221
- def test_display_multiple_values(self):
222
- self.pyscript_run(
223
- """
224
- <py-script>
225
- from pyscript import display
226
- hello = 'hello'
227
- world = 'world'
228
- display(hello, world)
229
- </py-script>
230
- """
231
- )
232
- inner_text = self.page.inner_text("html")
233
- assert inner_text == "hello\nworld"
234
-
235
- def test_display_multiple_append_false(self):
236
- self.pyscript_run(
237
- """
238
- <py-script>
239
- from pyscript import display
240
- display('hello', append=False)
241
- display('world', append=False)
242
- </py-script>
243
- """
244
- )
245
- inner_html = self.page.content()
246
- pattern = r'<py-script id="py-.*">world</py-script>'
247
- assert re.search(pattern, inner_html)
248
-
249
- # TODO: this is a display.py issue to fix when append=False is used
250
- # do not use the first element, just clean up and then append
251
- # remove the # display comment once that's done
252
- def test_display_multiple_append_false_with_target(self):
253
- self.pyscript_run(
254
- """
255
- <div id="circle-div"></div>
256
- <script type="py">
257
- from pyscript import display
258
- class Circle:
259
- r = 0
260
- def _repr_svg_(self):
261
- return (
262
- f'<svg height="{self.r*2}" width="{self.r*2}">'
263
- f'<circle cx="{self.r}" cy="{self.r}" r="{self.r}" fill="red" /></svg>'
264
- )
265
-
266
- circle = Circle()
267
-
268
- circle.r += 5
269
- # display(circle, target="circle-div", append=False)
270
- circle.r += 5
271
- display(circle, target="circle-div", append=False)
272
- </script>
273
- """
274
- )
275
- innerhtml = self.page.locator("id=circle-div").inner_html()
276
- assert (
277
- innerhtml
278
- == '<svg height="20" width="20"><circle cx="10" cy="10" r="10" fill="red"></circle></svg>' # noqa: E501
279
- )
280
- assert self.console.error.lines == []
281
-
282
- def test_display_list_dict_tuple(self):
283
- self.pyscript_run(
284
- """
285
- <py-script>
286
- from pyscript import display
287
- l = ['A', 1, '!']
288
- d = {'B': 2, 'List': l}
289
- t = ('C', 3, '!')
290
- display(l, d, t)
291
- </py-script>
292
- """
293
- )
294
- inner_text = self.page.inner_text("html")
295
- filtered_inner_text = filter_inner_text(inner_text)
296
- print(filtered_inner_text)
297
- assert (
298
- filtered_inner_text
299
- == "['A', 1, '!']\n{'B': 2, 'List': ['A', 1, '!']}\n('C', 3, '!')"
300
- )
301
-
302
- def test_display_should_escape(self):
303
- self.pyscript_run(
304
- """
305
- <py-script>
306
- from pyscript import display
307
- display("<p>hello world</p>")
308
- </py-script>
309
- """
310
- )
311
- # out = self.page.locator("py-script > div")
312
- node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
313
- node_list[0]
314
- # assert out.inner_html() == html.escape("<p>hello world</p>")
315
- # assert out.inner_text() == "<p>hello world</p>"
316
-
317
- @pytest.mark.skip("FIXME: HTML has been removed from pyscript")
318
- def test_display_HTML(self):
319
- self.pyscript_run(
320
- """
321
- <py-script>
322
- from pyscript import display, HTML
323
- display(HTML("<p>hello world</p>"))
324
- </py-script>
325
- """
326
- )
327
- # out = self.page.locator("py-script > div")
328
- node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
329
- node_list[0]
330
- # assert out.inner_html() == "<p>hello world</p>"
331
- # assert out.inner_text() == "hello world"
332
-
333
- @pytest.mark.skip(
334
- "FIX TEST: Works correctly in Chrome, but fails in TEST with the error:\n\n"
335
- "It's likely that the Test framework injections in config are causing"
336
- "this error."
337
- )
338
- def test_image_display(self):
339
- self.pyscript_run(
340
- """
341
- <py-config> packages = ["matplotlib"] </py-config>
342
- <py-script>
343
- from pyscript import display
344
- import matplotlib.pyplot as plt
345
- xpoints = [3, 6, 9]
346
- ypoints = [1, 2, 3]
347
- plt.plot(xpoints, ypoints)
348
- display(plt)
349
- </py-script>
350
- """
351
- )
352
- wait_for_render(self.page, "*", "<img src=['\"]data:image")
353
- test = self.page.wait_for_selector("img")
354
- img_src = test.get_attribute("src").replace(
355
- "data:image/png;charset=utf-8;base64,", ""
356
- )
357
- img_data = np.asarray(Image.open(io.BytesIO(base64.b64decode(img_src))))
358
- with Image.open(
359
- os.path.join(os.path.dirname(__file__), "test_assets", "line_plot.png"),
360
- ) as image:
361
- ref_data = np.asarray(image)
362
-
363
- deviation = np.mean(np.abs(img_data - ref_data))
364
- assert deviation == 0.0
365
- self.assert_no_banners()
366
-
367
- def test_empty_HTML_and_console_output(self):
368
- self.pyscript_run(
369
- """
370
- <py-script>
371
- from pyscript import display
372
- import js
373
- print('print from python')
374
- js.console.log('print from js')
375
- js.console.error('error from js');
376
- </py-script>
377
- """
378
- )
379
- inner_html = self.page.content()
380
- assert re.search("", inner_html)
381
- console_text = self.console.all.lines
382
- assert "print from python" in console_text
383
- assert "print from js" in console_text
384
- assert "error from js" in console_text
385
-
386
- def test_text_HTML_and_console_output(self):
387
- self.pyscript_run(
388
- """
389
- <py-script>
390
- from pyscript import display
391
- import js
392
- display('this goes to the DOM')
393
- print('print from python')
394
- js.console.log('print from js')
395
- js.console.error('error from js');
396
- </py-script>
397
- """
398
- )
399
- inner_text = self.page.inner_text("py-script")
400
- assert inner_text == "this goes to the DOM"
401
- assert self.console.log.lines[-2:] == [
402
- "print from python",
403
- "print from js",
404
- ]
405
- print(self.console.error.lines)
406
- assert self.console.error.lines[-1] == "error from js"
407
-
408
- def test_console_line_break(self):
409
- self.pyscript_run(
410
- """
411
- <py-script>
412
- print('1print\\n2print')
413
- print('1console\\n2console')
414
- </py-script>
415
- """
416
- )
417
- console_text = self.console.all.lines
418
- assert console_text.index("1print") == (console_text.index("2print") - 1)
419
- assert console_text.index("1console") == (console_text.index("2console") - 1)
420
-
421
- @pytest.mark.skip(
422
- "FIX TEST: Works correctly in Chrome, but fails in TEST with the error:\n\n"
423
- "It's likely that the Test framework injections in config are causing"
424
- "this error."
425
- )
426
- def test_image_renders_correctly(self):
427
- """This is just a sanity check to make sure that images are rendered correctly."""
428
- buffer = io.BytesIO()
429
- img = Image.new("RGB", (4, 4), color=(0, 0, 0))
430
- img.save(buffer, format="PNG")
431
-
432
- b64_img = base64.b64encode(buffer.getvalue()).decode("utf-8")
433
- expected_img_src = f"data:image/png;charset=utf-8;base64,{b64_img}"
434
-
435
- self.pyscript_run(
436
- """
437
- <py-config>
438
- packages = ["pillow"]
439
- </py-config>
440
-
441
- <div id="img-target" />
442
- <py-script>
443
- from pyscript import display
444
- from PIL import Image
445
- img = Image.new("RGB", (4, 4), color=(0, 0, 0))
446
- display(img, target='img-target', append=False)
447
- </py-script>
448
- """
449
- )
450
-
451
- rendered_img_src = self.page.locator("img").get_attribute("src")
452
- assert rendered_img_src == expected_img_src