Compare commits

...

70 Commits

Author SHA1 Message Date
Andrea Giammarchi
aa73d964ff Merge branch 'main' into reverted-4cores 2024-09-30 17:30:08 +02:00
Andrea Giammarchi
c257b704e6 Try to find out what we really need in CI (#2197) 2024-09-30 16:56:50 +02:00
webreflection
73409aca78 Try to find out what we really need in CI 2024-09-30 15:49:36 +02:00
Andrea Giammarchi
fc599d96a2 Use ubuntu-latest-8core everywhere (#2196) 2024-09-30 14:52:32 +02:00
Andrea Giammarchi
8de97a7e7b Updated coincident to use a local channel (#2195)
* Updated coincident to use a local channel
2024-09-30 14:03:33 +02:00
Nicholas Tollervey
2642be863b Fix sed search/replace when creating static page for a specific PyScript release. (#2194) 2024-09-30 11:13:02 +01:00
Nicholas Tollervey
9dad29ec17 Refactor repository. Fixes #2161 (#2192)
* Remove duplicate LICENSE.
* Remove un-userd pyscript.sw directory and its content.
* Remove ReadTheDocs settings (unused).
* Remove un-used pyproject.toml
* Remove now unused CHANGELOG. Changes now tracked via release notes on GitHub.
* Updated / cleaned release page template and associated GH actions.
* Update prettierignore to remove un-needed refs.
* Move troubleshooting into correct README.
* Add reason for the index.html
* Rename the "pyscript.core" directory to "core".
* Update PR template because CHANGELOG is no longer used.
* Codespell configuration in pyproject.toml.
* Update pyscript.core -> core in .githubignore
* Remove test-results/.last-run.json. This should be ignored by git.
* Pin nodejs version.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-30 10:29:26 +01:00
Nicholas Tollervey
abb1eb28fe Only use a single worker for playwright tests. (#2191) 2024-09-27 16:06:04 +01:00
Nicholas Tollervey
532ac0c80f Increase timeout for heavy-load test. (#2190) 2024-09-27 15:46:57 +01:00
Nicholas Tollervey
c1849d28e0 Bump python-minifier to latest version (supports most recent versions of Python). (#2189)
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-09-27 14:35:59 +01:00
Andrea Giammarchi
ad2af2392b Indirect refs counting (#2188) 2024-09-27 15:26:57 +02:00
Andrea Giammarchi
957ab69c21 Fix #2185 - Updated Polyscript and coincident (#2187) 2024-09-27 12:35:51 +02:00
Nicholas Tollervey
f5d49ee52c Test refinement. (#2183)
* Test refinement.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-26 12:02:25 +01:00
Nicholas Tollervey
06138bbb48 Update test suite (#2181)
* pyscript.web tests pass with upytest.
* Refactor of old integration tests to new Python tests.
* Added comprehensive test suite for Python based `pyscript` module.
* Add integration tests to Makefile (and CI)
* Remove un-needed upload action.
* Ensure fails are properly logged as an array. Remove the explicit test step, since this is already built into the build step.
* Bump polyscript.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-09-25 16:23:59 +01:00
Andrea Giammarchi
ae66d13d57 Fixed config issue via polyscript update (#2182) 2024-09-24 11:01:20 +02:00
Andrea Giammarchi
5aaeebf32c Make WebSocket lazy attributes definition possible (#2180) 2024-09-23 13:19:26 +02:00
Andrea Giammarchi
a6b0964185 Fix #2155 - Allow editor.process to run on behalf of users (#2177)
* Fix #2155 - Allow editor.process to run on behalf of users

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Updated dev/dependendencies

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-20 15:42:29 +02:00
Andrea Giammarchi
dd86169f2a Exposing config type once resolved and running (#2175) 2024-09-17 17:04:32 +02:00
Andrea Giammarchi
84c7d69db9 Remove useless test_report (#2169) 2024-09-15 14:29:55 +02:00
nasrin pathan
ca9b565adc Update README.md (#2170) 2024-09-15 14:29:03 +02:00
Andrea Giammarchi
b2d1018db1 Fix #2167 - Provide instructions to build the project (#2168)
* Fix #2167 - Provide instructions to build the project

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-13 16:58:32 +02:00
Nicholas Tollervey
c4e25d879e Update GH actions to node 20 and Python env for PyMinifier. (#2166)
* Update GH actions to node 20 and Python env for PyMinifier.

* Fix spaces.

* Fix test.yml

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-13 14:37:17 +01:00
Andrea Giammarchi
c82dbb755e cleanup npm package (#2163) 2024-09-13 15:04:21 +02:00
Andrea Giammarchi
1ed77321a5 Add a storage equivalent for JS (#2165)
* Add a storage equivalent for JS

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-13 12:05:40 +02:00
Andrea Giammarchi
e36a57eb06 Fix #2160 - Implement progress events (#2162) 2024-09-12 18:41:36 +02:00
Andrea Giammarchi
ee3cd76022 Follow up - Remove all innerHTML += for consistency sake (#2159) 2024-09-11 15:12:04 +02:00
Andrea Giammarchi
eb31e51a45 Fix #2150 - Avoid trashing previous added elements (#2151) 2024-09-11 11:37:42 +02:00
Andrea Giammarchi
c8c2dd0806 Avoid throwing if Pyodide does not await due missing arguments (#2158)
* Fix #2156 - Avoid requiring args for async functions

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-06 15:16:28 +02:00
Andrea Giammarchi
e525d54be0 Fix #2156 - Test @when with async listener (#2157) 2024-09-06 14:56:50 +02:00
Andrea Giammarchi
7b9f7c13f5 Improve by far error reporting around PyEditor bootstrap (#2153)
* Improve by far error reporting around PyEditor bootstrap

* Improve by far error reporting around PyEditor bootstrapiImprove by far error reporting around PyEditor bootstrap

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-04 16:52:08 +02:00
Andrea Giammarchi
7582cbef9c Fix #2146 - Workaround Pyodide issue with attributes (#2148)
* Fix #2146 - Workaround Pyodide issue with attributes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix #2146 - Updated polyscript

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-27 16:47:52 +02:00
Andrea Giammarchi
b395cde49c Forgot to build tests/index.html and push (#2144) 2024-08-26 12:29:16 +02:00
Andrea Giammarchi
9f46234f71 Fix #2114 - Cleanup the test folder + automation (#2143)
* Fix #2114 - Cleanup the test folder + automation
* Merged both test and tests into a single folder
2024-08-08 17:08:59 +02:00
Andrea Giammarchi
f4c4edeb29 Implemented pyminify for our stdlib (#2140)
* Implemented pyminify for our stdlib

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-07 12:33:20 +02:00
Andrea Giammarchi
7166c32384 Fix #2093 - Show setup errors with the editor (#2138)
* Fix #2093 - Show setup errors with the editor

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-06 16:31:42 +02:00
pre-commit-ci[bot]
ed126889ae [pre-commit.ci] pre-commit autoupdate (#2137)
updates:
- [github.com/psf/black: 24.4.2 → 24.8.0](https://github.com/psf/black/compare/24.4.2...24.8.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-06 12:36:00 +02:00
Andrea Giammarchi
0d0ea96435 Defaulting to async for top-level await (#2134) 2024-08-05 15:55:53 +02:00
Andrea Giammarchi
fafdf74007 Fixed async methods attached to window (#2136) 2024-08-05 11:58:22 +02:00
Martin
999897df12 The all-new, pyscript.web (ignore the branch name :) ) (#2129)
* Minor cleanups: move all Element classes to bottom of module.

* Commenting.

* Commenting.

* Commenting.

* Group dunder methods.

* Don't cache the element's parent.

* Remove style type check until we decide whether or not to add for classes too.

* Add ability to register/unregister element classes.

* Implement __iter__ for container elements.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Minor renaming to make it clear when we have an Element instance vs an actual DOM element.

* remove duplication: added Element.get_tag_name

* Commenting.

* Allow Element.append to 1) use *args, 2) accept iterables

* Remove iterable check - inteferes with js proxies.

* Don't use *args, so it quacks more like a list ;)

* Element.append take 2 :)

* Remove unused code.

* Move to web.py with a page object!

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added 'page.title' too :)

* Add __getitem__ as a shortcut for page.find

* Add Element.__getitem__ to be consistent

* Make __getitem__ consistent for Page, Element and ElementCollection.

* Docstringing.

* Docstringing.

* Docstringing/commenting.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix select.add (revert InnerHTML->html)

* Commenting.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Hand-edit some of the AI :)

* Rename ElementCollection.children -> ElementCollection.elements

* Remove unnecessary guard.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-01 10:36:57 +01:00
Andrea Giammarchi
d47fb58ede Update Pyodide to its 0.26.2 version (#2133) 2024-07-31 20:34:21 +02:00
Andrea Giammarchi
f316341e73 Updated Polyscript and added Panel worker test (#2130)
* Updated Polyscript and added Panel worker test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-31 14:31:35 +02:00
Andrea Giammarchi
8c46fcabf7 Updating polyscript to its latest (#2128)
* Updating polyscript to its latest
2024-07-29 16:59:31 +02:00
Martin
e4ff4d8fab Controversial version where Element just delegates to the underlying DOM element. (#2127)
* Update elements.py

* remove grid which allows simpler class tag mapping.
2024-07-24 13:27:12 -05:00
Martin
f20a0003ed fix: broken methods video.snap and canvas.download (#2126)
* fix: broken methods video.snap and canvas.download

* Allow canvas.draw to use actual image width/height.
2024-07-23 15:35:07 -05:00
Martin
6c938dfe3b Override __getattr__ and __setattr__ on ElementCollection. (#2116)
* Override __getattr__ and __setattr__ on ElementCollection.

* fix: bug when using a string to query an ElementCollection.

* Use Element.find when indexing ElementCollection with a string.

* For consistency also have a find method on ElementCollection.

* ElementCollection.find now returns a collection of collections :)

* fix tests: for textContent

* Revert to extend for ElementCollection.find :)

* Make element_from_dom a classmethod Element.from_dom

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* rename: Element.from_dom -> Element.from_dom_element

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* PyCharm warning sweep.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Workaround for mp not allowing setting via __dict__

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-22 10:32:37 -05:00
Andrea Giammarchi
d884586a82 Updated Polyscript to its latest (#2124) 2024-07-19 12:21:04 +02:00
Fabio Pliger
f8f7ba89c1 Cleanup pyscript web elements (#2094)
* change pydom example to use new pyscript.web namespace

* change tests to use new pyscript.web namespace

* create new pyscript.web package and move pydom to pyscript.web.dom

* add __init__ to pyscript.web and expose the dom instance instead of the pyscript.web.dom module

* move elements from pyweb.ui to pyscript.web and temp fix pydom import

* moved of elements file completed

* moved media from pyweb to pyscript.web

* RIP pyweb

* move JSProperty from pyscript.web.dom to pyscript.web.elements

* move element classes from pyscript.web.dom to pyscript.web.elements

* first round of fixes while running tests

* fix test typo

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* restore right type type returned for Element.parent. ALL TESTS PASS LOCALLY NOW

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* clean up dom.py from dead commented code and osbolete comments

* bugfix: dom shouldn't return None when it can't find any element for a specific selector so it now returns an empty collection

* additional cleanup in tests

* lint

* initial cleaning up of unused modules

* change element.append to not consider types anymore and add tests for appending elements.Element or a JsProxy object

* add Element.append tests for append JS elements directly and appending nodeList as well

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Tag and create the correct subclass of Element.

* Move: Element.snap -> video.snap

* Move: Element.download and draw to canvas.download and draw.

* Minor cleanups.

* Commenting.

* Allow css classes to be passed to Element constructor.

* Commenting.

* Typo fix.

* Make html, id and text JSProperties.

* Commenting.

* Remove unnecessary selected attribute on BaseElement.

* Extract: BaseElement.from_js -> element_from_js

* Pass *args and **kwargs to element_from_js and remove BaseElement.create

* Move value attribute to specific Element subclasses.

* fix: wrapping of existing js elements.

* Add body and head elements so parent and children work everywhere.

* Revert order of HasOptions mixin for the select element.

* Comment out tests that are no longer relevant (see comment).

* Use correct super args in mixin.

* Have to use element_from_js when returning options from OptionsProxy.

* rename: StyleProxy -> Style, OptionsProxy -> Options and added Classes.

* Remove cached_property.

* Remove list-y methods from Classes collection.

* Allow explicit children or *args for containers.

* controversial: fix tests to use find rather than dom

* Add html element so (say) body.parent does what is expected.

* Collapse Element class hierarchy.

* rename: js_element -> dom_element

* rename: element_from_js -> element_from_dom

* replace: JS with DOM

* rename: _js -> _dom_element

* fix dom tests.

* Complete element list with void elements derived from Element.

* Added attributes to the newly added Element subclasses.

* remove dom module, replace with instance.

Also, remove media :)

* fix: typo in test for 'b' element.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove dom and media modules.

* fix up ts definitions.

* Added missing import (used in content property).

* Added TODO :)

* wip: Add ClassesCollection

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Attempt to ask black to leave class list alone.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add classes attribute to ElementCollection

* wip: work on classes collection

* Extract code to get set of all class names in ClassesCollection.

* Update elements.py

* Polishing.

* Make children return an ElementCollection

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* wip: Add the ability to set multiple properties.

* Add __getitem__ back to the dom object.

* Put validation when setting DOM properties back in.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* All tests green.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove unnecessary comment.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Martin <martin.chilvers@gmail.com>
2024-07-03 14:21:23 -07:00
Andrea Giammarchi
67d47511d5 Fix MicroPython terminal input when no REPL is used/needed (#2113)
* Fix terminal input when no REPL is used/needed
* Fix input backspace too
2024-07-03 13:03:31 +02:00
Andrea Giammarchi
6f49f18937 Updated Polyscript with its workers feature (#2104)
* Updated Polyscript with its workers feature
* Worked around the inconsistent behavior between Pyodide and MicroPython
* Fixed Pyodide greedy access to undesired Proxy fields
2024-06-26 14:01:22 +02:00
Andrea Giammarchi
7b8ef7ebe2 Fix #2109 - Allow inline JSON config attribute in PyEditor (#2110)
Fix #2109 - Allow inline JSON config attribute in PyEditor
2024-06-24 17:04:28 +02:00
Andrea Giammarchi
461ae38763 Updated reference code to grab latest (#2107) 2024-06-21 16:06:18 +02:00
Andrea Giammarchi
4b90ebdef5 Bring back pyweb as it was (#2105) 2024-06-21 14:49:20 +02:00
Andrea Giammarchi
15c19aa708 Updated Polyscript with latest MicroPython (#2103) 2024-06-19 17:56:22 +02:00
Andrea Giammarchi
d0406be84c A persistent IndexedDB store for PyScript (#2101)
A persistent IndexedDB store for PyScript
2024-06-19 14:11:57 +02:00
Andrea Giammarchi
aab015b9b8 Better py editor indentation (#2098)
Better PyEditor Indentation
2024-06-13 11:34:14 +02:00
Andrea Giammarchi
a1e5a05b49 PyEditor cumulative fixes & improvements (#2095)
* PyEditor fixes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* PyEditor cumulative fixes & improvements

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-12 18:55:36 +02:00
Fabio Pliger
f1a787e031 move pydom and elements from pyweb to pyscript.web (#2092)
* change pydom example to use new pyscript.web namespace

* change tests to use new pyscript.web namespace

* create new pyscript.web package and move pydom to pyscript.web.dom

* add __init__ to pyscript.web and expose the dom instance instead of the pyscript.web.dom module

* move elements from pyweb.ui to pyscript.web and temp fix pydom import

* moved of elements file completed

* moved media from pyweb to pyscript.web

* RIP pyweb

* move JSProperty from pyscript.web.dom to pyscript.web.elements

* move element classes from pyscript.web.dom to pyscript.web.elements

* first round of fixes while running tests

* fix test typo

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* restore right type type returned for Element.parent. ALL TESTS PASS LOCALLY NOW

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* clean up dom.py from dead commented code and osbolete comments

* bugfix: dom shouldn't return None when it can't find any element for a specific selector so it now returns an empty collection

* additional cleanup in tests

* lint

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-06 15:42:14 +02:00
Fabio Pliger
b41cfb7b60 UI creation API (#1960)
* add JSProperty to pydom so it can be used to create descriptors mapping to specific JS attributes

* add pyweb.ui

* fix pyweb imports

* fix link and a elements and add a script element

* fix imports and add initialization to load resources to shoelace module

* new pyweb.ui test folder

* remove comments

* add Icon to shoelace components

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* use html property rather than accessing _js directly

* add markdown suppport

* move examples section out of stdlib pyweb to examples.py in the demo itself

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* simplify demo code

* improve docstrings

* precommit fixes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* simplify code for main page

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add load_resources to markdown

* add showlace extra style dynamically

* precommit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add gallery files

* add global attributes and dynamic property assignment

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add shoelace radio component (#1961)

* add shoelace radio component

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* fix type that lead using the the JSDescriptor directly instead of the factory method

* add missing marked dependency

* refactor gallary to simplify codebase

* precommig lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add text attribute to pydom Elements

* add global JS attributes to elements and improve demos

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix image instantiation on card since the API has changed

* add attributes to all classes in elements

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* change example creation functions to take the label and the object directly

* fix input name clashing with input keyword

* refactor examples to better simplify and automate

* fix div clashing names

* fix demo left menu width

* simplify base elements demo by moving all the examples to the examples module

* rename Grid to grid to align to other elements

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* reorg classes order in elements.py and add missing elements to examples

* fix issue related to now importing div from pyweb.ui

* improve demo

* link and fix spelling typo

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add a bunch more elements (#1966)

* Add copy button

* Add skeleton and spinner

* Add Switch

* Add text area

* Add more elements

* More styling to each component

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com>

* Add radio group (#1963)

* add radio group

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com>

* Small tweaks to main demo page (#1962)

* Small tweaks to main demo page

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com>

* fix post merge issues

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed issues with renaming Grid to grid, after we merged

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add multple HTML elements in alphabetical order from abbr to em

* fix attributes of some of the elements added in the previous commit and add embed

* fix embed attributes and add fieldset

* add figcation, figure and form. Also fix ordering of definitoin of img and input_

* add style and lint

* wrap up adding all major html elements

* fix test failing due to different error message from fake server compared to a real test server

* change default docstring associated with all classes dynamically patched

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add pyweb tests

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add global attributes and change abbr test

* fix abbr to inherit from TextElementBase

* add address test and improve error messaging when ElementBase gets a bad input as style

* change test helper function to be more  flexible on attributes and manage content vs non content based elements. Also adds area tests

* add test for more elements up to caption

* fix canvas and caption as elements that have content and fix name typo on figcaption

* fix another typo on figcaption

* minor fixes and complete tests for all elements

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* adapt shoelace to latest upates in ui.elements

* fix issue with example code not showing created button

* move global attributes to base class

* replace all the calls to _add_js_properties with defining attributes directly in the classes

* finish moving all properties manually on each class

* remove _add_js_properties

* replace JSProperty with js_property

* replace JSProperty with js_property

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed merge conflicts

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove js_property and just use JSProperty with name and allow_nones as arguments

* fix bug around Element not being able to map global attributes in subclasses

* remove js_property and fix references

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* precommit

* precommit again

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* enable pyweb on micropython

* switch examples to micropython

* fix pydom issue with micropython, created by the monkeypatch around JsProxy

* print micropython version on micropython pydom example

* lint and remove of textwrap in stdlib for micropython compatibility

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* added msising attributes on the option Element. Tests are all passing now

* fix tests

* lint

* temp ugly fix for micropython, using the when decorator with a function without arguments

* small fixes and improvements to examples

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix examples and broken link from the removal of markdown and shoelace from stdlib

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* dynamically change the server address in tests

* use the right element in test_a

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* skipping test_audio in CI due to different behavior of fake_server vs a real server, that runs in local tests

* add conditional expected_missing_file_errors property to manage different behaviour between local tests and CI (due to fake_server)

* lint

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Askat <aaskat@users.noreply.github.com>
Co-authored-by: Fábio Rosado <hello@fabiorosado.dev>
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-06-04 13:30:34 -07:00
Andrea Giammarchi
1c675307e1 Expose pyscript.py_import and js_import for lazy Python/JS modules (#2091)
* Expose pyscript.py_modules for lazy Python modules

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-04 19:08:52 +02:00
pre-commit-ci[bot]
ac56f82c6d [pre-commit.ci] pre-commit autoupdate (#2089)
updates:
- [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-04 09:36:04 +02:00
Andrea Giammarchi
2ac5ca79d7 Expose py-editor code content read/write (#2087)
* Expose py-editor code content read/write

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-03 12:32:54 +02:00
Jeff Glass
cb9ee6f7e2 Update README.md, 2023.11.2 -> 2024.5.2 (#2085) 2024-06-03 10:14:27 +02:00
Andrea Giammarchi
9abaef33bd Fix #2067 - Enable .whl packages (#2084) 2024-05-31 11:50:30 +02:00
Brendan
320a537db2 Add step in build process to publish a tarfile (#2077)
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-05-30 16:25:58 +02:00
Andrea Giammarchi
9b775ce015 Enhance MicroPython Terminal on both Main and Worker (#2083)
* Allow MicroPython Terminal on Main

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-05-30 13:36:42 +02:00
jdw170000
66f72eda1e Add spinner to py-editor run buttons (#2078)
* Add spinner to disabled py-editor run buttons

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* css nit suggested by WebReflection

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-05-29 17:36:06 +02:00
Martin
39ca29749c fix: typo in "isinstance" in Element.snap. (#2071)
Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com>
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-05-27 12:08:49 +02:00
Amjith Ramanujam
85da548447 Fix the links to development process. (#2064)
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-05-27 11:47:25 +02:00
jdw170000
9985787e4b Update dependencies and deprecated linter configuration format (#2076)
* update python dependencies to latest versions

* isort automatic formatting nits

* update eslint config to non-deprecated flat format

* `npm update` to upgrade javascript dependencies

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-05-27 11:44:24 +02:00
Takanori Suzuki
18ec6ce775 refs #2068 fix links on CONTRIBUTING.md (#2072) 2024-05-21 10:36:41 +01:00
281 changed files with 6983 additions and 10058 deletions

View File

@@ -11,5 +11,4 @@
<!-- Note: Only user-facing changes require a changelog entry. Internal-only API changes do not require a changelog entry. Changes in documentation do not require a changelog entry. -->
- [ ] All tests pass locally
- [ ] I have updated `CHANGELOG.md`
- [ ] I have created documentation for this(if applicable)
- [ ] I have created / updated documentation for this (if applicable)

View File

@@ -7,11 +7,11 @@ on:
defaults:
run:
working-directory: ./pyscript.core
working-directory: ./core
jobs:
prepare-release:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -19,7 +19,22 @@ jobs:
- name: Install node
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- name: Python venv
run: python -m venv env
- name: Activate Python
run: source env/bin/activate
- name: Update pip
run: pip install --upgrade pip
- name: Install PyMinifier
run: pip install --ignore-requires-python python-minifier
- name: Install Setuptools
run: pip install setuptools
- name: Cache node modules
uses: actions/cache@v4
@@ -35,14 +50,14 @@ jobs:
${{ runner.os }}-
- name: NPM Install
run: npm install && npx playwright install
run: npm install && npx playwright install chromium
- name: Build
run: npm run build
- name: Generate index.html
working-directory: .
run: sed 's#_PATH_#./#' ./public/index.html > ./pyscript.core/dist/index.html
run: sed -e 's#_PATH_#./#' -e 's#_DOC_VERSION_#latest#' -e 's#_TAG_VERSION_##' -e 's#_VERSION_#latest#' ./public/index.html > ./core/dist/index.html
- name: Zip dist folder
run: zip -r -q ./build.zip ./dist

View File

@@ -6,11 +6,11 @@ on:
defaults:
run:
working-directory: ./pyscript.core
working-directory: ./core
jobs:
publish-release:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
permissions:
id-token: write
contents: read
@@ -21,7 +21,22 @@ jobs:
- name: Install node
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- name: Python venv
run: python -m venv env
- name: Activate Python
run: source env/bin/activate
- name: Update pip
run: pip install --upgrade pip
- name: Install PyMinifier
run: pip install --ignore-requires-python python-minifier
- name: Install Setuptools
run: pip install setuptools
- name: Cache node modules
uses: actions/cache@v4
@@ -37,14 +52,18 @@ jobs:
${{ runner.os }}-
- name: npm install
run: npm install && npx playwright install
run: npm install && npx playwright install chromium
- name: build
run: npm run build
- name: Generate index.html in snapshot
working-directory: .
run: sed 's#_PATH_#https://pyscript.net/releases/${{ github.ref_name }}/#' ./public/index.html > ./pyscript.core/dist/index.html
run: sed -e 's#_PATH_#https://pyscript.net/releases/${{ github.ref_name }}/#' -e 's#_DOC_VERSION_#${{ github.ref_name }}#' -e 's#_TAG_VERSION_#/tag/${{ github.ref_name }}#' -e 's#_VERSION_#${{ github.ref_name }}#' ./public/index.html > ./core/dist/index.html
- name: Generate release.tar from snapshot and put it in dist/
working-directory: .
run: tar -cvf ../release.tar * && mv ../release.tar .
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4

View File

@@ -10,11 +10,11 @@ on:
defaults:
run:
working-directory: ./pyscript.core
working-directory: ./core
jobs:
publish-snapshot:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
permissions:
contents: read
id-token: write
@@ -25,7 +25,22 @@ jobs:
- name: Install node
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- name: Python venv
run: python -m venv env
- name: Activate Python
run: source env/bin/activate
- name: Update pip
run: pip install --upgrade pip
- name: Install PyMinifier
run: pip install --ignore-requires-python python-minifier
- name: Install Setuptools
run: pip install setuptools
- name: Cache node modules
uses: actions/cache@v4
@@ -41,7 +56,7 @@ jobs:
${{ runner.os }}-
- name: Install Dependencies
run: npm install && npx playwright install
run: npm install && npx playwright install chromium
- name: Build Pyscript.core
run: npm run build
@@ -54,7 +69,7 @@ jobs:
- name: Generate index.html in snapshot
working-directory: .
run: sed 's#_PATH_#https://pyscript.net/snapshots/${{ inputs.snapshot_version }}/#' ./public/index.html > ./pyscript.core/dist/index.html
run: sed -e 's#_PATH_#https://pyscript.net/snapshots/${{ inputs.snapshot_version }}/#' -e 's#_DOC_VERSION_#${{ inputs.snapshot_version }}#' -e 's#_TAG_VERSION_#/tag/${{ inputs.snapshot_version }}#' -e 's#_VERSION_#${{ inputs.snapshot_version }}#' ./public/index.html > ./core/dist/index.html
- name: Copy to Snapshot
run: >

View File

@@ -1,24 +1,24 @@
name: "Publish Unstable"
on:
push: # Only run on merges into main that modify files under pyscript.core/ and examples/
push: # Only run on merges into main that modify files under core/ and examples/
branches:
- main
paths:
- pyscript.core/**
- core/**
- examples/**
workflow_dispatch:
jobs:
publish-unstable:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
permissions:
id-token: write
contents: read
defaults:
run:
working-directory: ./pyscript.core
working-directory: ./core
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -26,7 +26,22 @@ jobs:
- name: Install node
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- name: Python venv
run: python -m venv env
- name: Activate Python
run: source env/bin/activate
- name: Update pip
run: pip install --upgrade pip
- name: Install PyMinifier
run: pip install --ignore-requires-python python-minifier
- name: Install Setuptools
run: pip install setuptools
- name: Cache node modules
uses: actions/cache@v4
@@ -42,14 +57,14 @@ jobs:
${{ runner.os }}-
- name: NPM Install
run: npm install && npx playwright install
run: npm install && npx playwright install chromium
- name: Build
run: npm run build
- name: Generate index.html in snapshot
working-directory: .
run: sed 's#_PATH_#https://pyscript.net/unstable/#' ./public/index.html > ./pyscript.core/dist/index.html
run: sed -e 's#_PATH_#./#' -e 's#_DOC_VERSION_#latest#' -e 's#_TAG_VERSION_##' -e 's#_VERSION_#latest#' ./public/index.html > ./core/dist/index.html
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4

View File

@@ -5,20 +5,20 @@ on:
branches:
- main
paths:
- pyscript.core/**
- core/**
- .github/workflows/test.yml
pull_request: # Only run on merges into main that modify certain files
branches:
- main
paths:
- pyscript.core/**
- core/**
- .github/workflows/test.yml
workflow_dispatch:
jobs:
BuildAndTest:
runs-on: ubuntu-latest-8core
runs-on: ubuntu-latest-4core
env:
MINICONDA_PYTHON_VERSION: py38
MINICONDA_VERSION: 4.11.0
@@ -69,24 +69,12 @@ jobs:
make setup
- name: Build
run: make build
- name: Integration Tests
#run: make test-integration-parallel
run: |
make test-integration
run: make build # Integration tests run in the build step.
- uses: actions/upload-artifact@v4
with:
name: pyscript
path: |
pyscript.core/dist/
core/dist/
if-no-files-found: error
retention-days: 7
- uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: test_results
path: test_results/
if-no-files-found: error

View File

@@ -1,16 +0,0 @@
name: Test Report
on:
workflow_run:
workflows: ['\[CI\] Test']
types:
- completed
jobs:
report:
runs-on: ubuntu-latest-8core
steps:
- uses: dorny/test-reporter@v1.9.0
with:
artifact: test_results
name: Test reports
path: "*.xml"
reporter: java-junit

15
.gitignore vendored
View File

@@ -142,10 +142,11 @@ coverage/
test_results
# @pyscript/core npm artifacts
pyscript.core/core.*
pyscript.core/dist
pyscript.core/dist.zip
pyscript.core/src/plugins.js
pyscript.core/src/stdlib/pyscript.js
pyscript.core/src/3rd-party/*
!pyscript.core/src/3rd-party/READMEmd
core/test-results/*
core/core.*
core/dist
core/dist.zip
core/src/plugins.js
core/src/stdlib/pyscript.js
core/src/3rd-party/*
!core/src/3rd-party/READMEmd

View File

@@ -21,17 +21,17 @@ repos:
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
exclude: pyscript\.core/dist|\.min\.js$
exclude: core/dist|\.min\.js$
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 24.4.2
rev: 24.8.0
hooks:
- id: black
exclude: pyscript\.core/src/stdlib/pyscript/__init__\.py
exclude: core/src/stdlib/pyscript/__init__\.py
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
rev: v2.3.0
hooks:
- id: codespell # See 'pyproject.toml' for args
exclude: \.js\.map$
@@ -42,7 +42,7 @@ repos:
rev: "v3.0.0-alpha.6"
hooks:
- id: prettier
exclude: pyscript\.core/test|pyscript\.core/dist|pyscript\.core/types|pyscript.core/src/stdlib/pyscript.js|pyscript\.sw/|pyscript.core/src/3rd-party
exclude: core/test|core/dist|core/types|core/src/stdlib/pyscript.js|pyscript\.sw/|core/src/3rd-party
args: [--tab-width, "4"]
- repo: https://github.com/pycqa/isort

View File

@@ -1,5 +1,3 @@
ISSUE_TEMPLATE
*.min.*
package-lock.json
docs
examples/panel.html

View File

@@ -1,28 +0,0 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: miniconda3-4.7
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
conda:
environment: docs/environment.yml
# If using Sphinx, optionally build your docs in additional formats such as PDF
# formats:
# - pdf
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt

View File

@@ -1,87 +0,0 @@
# Release Notes
## 2023.05.01
### Features
- Added the `xterm` attribute to `py-config`. When set to `True` or `xterm`, an (output-only) [xterm.js](http://xtermjs.org/) terminal will be used in place of the default py-terminal.
- The default version of Pyodide is now `0.23.2`. See the [Pyodide Changelog](https://pyodide.org/en/stable/project/changelog.html#version-0-23-2) for a detailed list of changes.
- Added the `@when` decorator for attaching Python functions as event handlers
- The `py-mount` attribute on HTML elements has been deprecated, and will be removed in a future release.
#### Runtime py- attributes
- Added logic to react to `py-*` attributes changes, removal, `py-*` attributes added to already live nodes but also `py-*` attributes added or defined via injected nodes (either appended or via `innerHTML` operations). ([#1435](https://github.com/pyscript/pyscript/pull/1435))
#### &lt;script type="py"&gt;
- Added the ability to optionally use `<script type="py">`, `<script type="pyscript">` or `<script type="py-script">` instead of a `<py-script>` custom element, in order to tackle cases where the content of the `<py-script>` tag, inevitably parsed by browsers, could accidentally contain _HTML_ able to break the surrounding page layout. ([#1396](https://github.com/pyscript/pyscript/pull/1396))
#### &lt;py-terminal&gt;
- Added a `docked` field and attribute for the `<py-terminal>` custom element, enabled by default when the terminal is in `auto` mode, and able to dock the terminal at the bottom of the page with auto scroll on new code execution.
#### &lt;py-script&gt;
- Restored the `output` attribute of `py-script` tags to route `sys.stdout` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
- Added a `stderr` attribute of `py-script` tags to route `sys.stderr` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
#### &lt;py-repl&gt;
- The `output` attribute of `py-repl` tags now specifies the id of the DOM element that `sys.stdout`, `sys.stderr`, and the results of a REPL execution are written to. It no longer affects the location of calls to `display()`
- Added a `stderr` attribute of `py-repl` tags to route `sys.stderr` to a DOM element with the given ID. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
- Resored the `output-mode` attribute of `py-repl` tags. If `output-mode` == 'append', the DOM element where output is printed is _not_ cleared before writing new results.
- Load code from the attribute src of py-repl and preload it into the corresponding py-repl tag by use the attribute `str` in your `py-repl` tag([#1292](https://github.com/pyscript/pyscript/pull/1292))
- &lt;py-repl&gt; elements now have a `getPySrc()` method, which returns the code inside the REPL as a string.([#1516](https://github.com/pyscript/pyscript/pull/1292))
#### Plugins
- Plugins may now implement the `beforePyReplExec()` and `afterPyReplExec()` hooks, which are called immediately before and after code in a `py-repl` tag is executed. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
#### Web worker support
- introduced the new experimental `execution_thread` config option: if you set `execution_thread = "worker"`, the python interpreter runs inside a web worker
- worker support is still **very** experimental: not everything works, use it at your own risk
### Bug fixes
- Fixes [#1280](https://github.com/pyscript/pyscript/issues/1280), which describes the errors on the PyRepl tests related to having auto-gen tags that shouldn't be there.
### Enhancements
- Py-REPL tests now run on both osx and non osx OSs
- migrated from _rollup_ to _esbuild_ to create artifacts
- updated `@codemirror` dependency to its latest
### Docs
- Add docs for event handlers
## 2023.03.1
### Features
### Bug fixes
- Fixed an issue where `pyscript` would not be available when using the minified version of PyScript. ([#1054](https://github.com/pyscript/pyscript/pull/1054))
- Fixed missing closing tag when rendering an image with `display`. ([#1058](https://github.com/pyscript/pyscript/pull/1058))
- Fixed a bug where Python plugins methods were being executed twice. ([#1064](https://github.com/pyscript/pyscript/pull/1064))
### Enhancements
- When adding a `py-` attribute to an element but didn't added an `id` attribute, PyScript will now generate a random ID for the element instead of throwing an error which caused the splash screen to not shutdown. ([#1122](https://github.com/pyscript/pyscript/pull/1122))
- You can now disable the splashscreen by setting `enabled = false` in your `py-config` under the `[splashscreen]` configuration section. ([#1138](https://github.com/pyscript/pyscript/pull/1138))
### Documentation
- Fixed 'Direct usage of document is deprecated' warning in the getting started guide. ([#1052](https://github.com/pyscript/pyscript/pull/1052))
- Added reference documentation for the `py-splashscreen` plugin ([#1138](https://github.com/pyscript/pyscript/pull/1138))
- Adds doc for installing tests ([#1156](https://github.com/pyscript/pyscript/pull/1156))
- Adds docs for custom Pyscript attributes (`py-*`) that allow you to add event listeners to an element ([#1125](https://github.com/pyscript/pyscript/pull/1125))
### Deprecations and Removals
- The py-config `runtimes` to specify an interpreter has been deprecated. The `interpreters` config should be used instead. ([#1082](https://github.com/pyscript/pyscript/pull/1082))
- The attributes `pys-onClick` and `pys-onKeyDown` have been deprecated, but the warning was only shown in the console. An alert banner will now be shown on the page if the attributes are used. They will be removed in the next release. ([#1084](https://github.com/pyscript/pyscript/pull/1084))
- The pyscript elements `py-button`, `py-inputbox`, `py-box` and `py-title` have now completed their deprecation cycle and have been removed. ([#1084](https://github.com/pyscript/pyscript/pull/1084))
- The attributes `pys-onClick` and `pys-onKeyDown` have been removed. Use `py-click` and `py-keydown` instead ([#1361](https://github.com/pyscript/pyscript/pull/1361))

View File

@@ -59,9 +59,9 @@ If you would like to contribute to PyScript, but you aren't sure where to begin,
## Setting up your local environment and developing
If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://pyscript.github.io/docs/latest/development/setting-up-environment.html) will help you get started.
If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://docs.pyscript.net/latest/contributing/#set-up-your-development-environment) will help you get started.
You can also read about PyScript's [development process](https://pyscript.github.io/docs/latest/development/developing.html) to learn how to contribute code to PyScript, how to run tests and what's the PR etiquette of the community!
You can also read about PyScript's [development process](https://docs.pyscript.net/latest/developers/) to learn how to contribute code to PyScript, how to run tests and what's the PR etiquette of the community!
## License terms for contributions

View File

@@ -12,7 +12,7 @@ all:
@echo "make clean - clean up auto-generated assets."
@echo "make build - build PyScript."
@echo "make precommit-check - run the precommit checks (run eslint)."
@echo "make test-integration - run all integration tests sequentially."
@echo "make test - run all automated tests in playwright."
@echo "make fmt - format the code."
@echo "make fmt-check - check the code formatting.\n"
@@ -39,13 +39,12 @@ check-python:
# Check the environment, install the dependencies.
setup: check-node check-npm check-python
cd pyscript.core && npm install && cd ..
cd core && npm install && cd ..
ifeq ($(VIRTUAL_ENV),)
echo "\n\n\033[0;31mCannot install Python dependencies. Your virtualenv is not activated.\033[0m"
false
else
python -m pip install -r requirements.txt
playwright install
endif
# Clean up generated assets.
@@ -56,21 +55,15 @@ clean:
# Build PyScript.
build:
cd pyscript.core && npx playwright install && npm run build
cd core && npx playwright install chromium && npm run build
# Run the precommit checks (run eslint).
precommit-check:
pre-commit run --all-files
# Run all integration tests sequentially.
test-integration:
mkdir -p test_results
pytest -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml
# Run all integration tests in parallel.
test-integration-parallel:
mkdir -p test_results
pytest --numprocesses auto -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml
# Run all automated tests in playwright.
test:
cd core && npm run test:integration
# Format the code.
fmt: fmt-py

View File

@@ -38,11 +38,11 @@ To try PyScript, import the appropriate pyscript files into the `<head>` tag of
<head>
<link
rel="stylesheet"
href="https://pyscript.net/releases/2023.11.2/core.css"
href="https://pyscript.net/releases/2024.8.2/core.css"
/>
<script
type="module"
src="https://pyscript.net/releases/2023.11.2/core.js"
src="https://pyscript.net/releases/2024.8.2/core.js"
></script>
</head>
<body>
@@ -67,9 +67,11 @@ Check out the [official docs](https://docs.pyscript.net/) for more detailed docu
## How to Contribute
Read the [contributing guide](CONTRIBUTING.md) to learn about our development process, reporting bugs and improvements, creating issues and asking questions.
Read the [contributing guide](https://docs.pyscript.net/latest/contributing/) to learn about our development process, reporting bugs and improvements, creating issues and asking questions.
Check out the [developing process](https://pyscript.github.io/docs/latest/contributing) documentation for more information on how to setup your development environment.
Check out the [developing process](https://docs.pyscript.net/latest/developers/) documentation for more information on how to setup your development environment.
For technical details of the code, please see the [README](pyscript.core/README) in `pyscript.core`.
## Governance

View File

@@ -1,19 +0,0 @@
# Troubleshooting
This page is meant for troubleshooting common problems with PyScript.
## Table of contents:
- [Make Setup](#make-setup)
## Make setup
A lot of problems related to `make setup` are related to node and npm being outdated. Once npm and node are updated, `make setup` should work. You can follow the steps on the [npm documentation](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) to update npm (the update command for Linux should work for Mac as well). Once npm has been updated you can continue to the instructions to update node below.
To update Node run the following commands in order (Most likely you'll be prompted for your user password, this is normal):
```
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
```

View File

@@ -4,15 +4,15 @@ We have moved and renamed previous _core_ module as [polyscript](https://github.
## Documentation
Please read [core documentation](./docs/README.md) to know more about this project.
Please read [core documentation](https://docs.pyscript.net/) to know more about this project.
## Development
Clone this repository then run `npm install` within its folder.
Clone this repository then run `npm install` within this folder.
Use `npm run build` to create all artifacts and _dist_ files.
Use `npm run server` to test locally, via the `http://localhost:8080/test/` url, smoke tests or to test manually anything you'd like to check.
Use `npm run server` to test locally, via the `http://localhost:8080/tests/` url, smoke tests or to test manually anything you'd like to check.
### Artifacts
@@ -35,15 +35,37 @@ Before running the tests, we need to create a tests environment first. To do so
make setup
```
This will create a tests environment [in the root of the project, named `./env`]and install all the dependencies needed to run the tests.
This will create a tests environment [in the root of the project, named `./env`] and install all the dependencies needed to run the tests.
After the command has completed and the tests environment has been created, you can run the **integration tests** with
A lot of problems related to `make setup` are related to node and npm being outdated. Once npm and node are updated, `make setup` should work. You can follow the steps on the [npm documentation](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) to update npm (the update command for Linux should work for Mac as well). Once npm has been updated you can continue to the instructions to update node below.
To update Node run the following commands in order (most likely you'll be prompted for your user password, this is normal):
```
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
```
After the `make setup` command has completed, you can run the **automated tests** with
the following command:
```
make test-integration
make test
```
(This essentially runs the `npm run test:integration` command in the right place. This is defined in PyScript's `package.json` file.)
Tests are found in the `tests` directory. These are organised into three locations:
1. `python` - the Python based test suite to exercise Python code **within** PyScript.
2. `javascript` - JavaScript tests to exercise PyScript itself, in the browser.
3. `manual` - containing tests to run manually in a browser, due to the complex nature of the tests.
We use [Playwright](https://playwright.dev/) to automate the running of the Python and JavaScript test suites. We use [uPyTest](https://github.com/ntoll/upytest) as a test framework for the Python test suite. uPyTest is a "PyTest inspired" framework for running tests in the browser on both MicroPython and Pyodide.
The automated (Playwright) tests are specified in the `tests/integration.spec.js` file.
## `pyscript` python package
The `pyscript` package available in _Python_ lives in the folder `src/stdlib/pyscript/`.

22
core/eslint.config.mjs Normal file
View File

@@ -0,0 +1,22 @@
import globals from "globals";
import js from "@eslint/js";
export default [
js.configs.recommended,
{
ignores: ["**/3rd-party/"],
},
{
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser,
...globals.es2021,
},
},
rules: {
"no-implicit-globals": ["error"],
},
},
];

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@pyscript/core",
"version": "0.4.34",
"version": "0.5.16",
"type": "module",
"description": "PyScript",
"module": "./index.js",
@@ -8,6 +8,18 @@
"jsdelivr": "./jsdelivr.js",
"browser": "./index.js",
"main": "./index.js",
"engines": {
"node": ">=20"
},
"files": [
"./dist/",
"./src/",
"./types/",
"./index.js",
"./jsdelivr.js",
"LICENSE",
"README.md"
],
"exports": {
".": {
"types": "./types/core.d.ts",
@@ -16,18 +28,23 @@
"./css": {
"import": "./dist/core.css"
},
"./storage": {
"import": "./dist/storage.js"
},
"./package.json": "./package.json"
},
"scripts": {
"server": "npx static-handler --coi .",
"build": "export ESLINT_USE_FLAT_CONFIG=false; npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && eslint src/ && npm run ts && npm run test:mpy",
"server": "echo \"➡️ TESTS @ $(tput bold)http://localhost:8080/tests/$(tput sgr0)\"; npx static-handler --coi .",
"build": "export ESLINT_USE_FLAT_CONFIG=true;npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && npm run build:tests-index && if [ -z \"$NO_MIN\" ]; then eslint src/ && npm run ts && npm run test:integration; fi",
"build:core": "rm -rf dist && rollup --config rollup/core.config.js && cp src/3rd-party/*.css dist/",
"build:flatted": "node rollup/flatted.cjs",
"build:plugins": "node rollup/plugins.cjs",
"build:stdlib": "node rollup/stdlib.cjs",
"build:3rd-party": "node rollup/3rd-party.cjs",
"build:tests-index": "node rollup/build_test_index.cjs",
"clean:3rd-party": "rm src/3rd-party/*.js && rm src/3rd-party/*.css",
"test:mpy": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test --fully-parallel test/mpy.spec.js || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
"test:ws": "bun test/ws/index.js & playwright test test/ws.spec.js",
"test:integration": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test tests/js_tests.spec.js tests/py_tests.spec.js || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
"test:ws": "bun tests/ws/index.js & playwright test tests/ws.spec.js",
"dev": "node dev.cjs",
"release": "npm run build && npm run zip",
"size": "echo -e \"\\033[1mdist/*.js file size\\033[0m\"; for js in $(ls dist/*.js); do cat $js | brotli > ._; echo -e \"\\033[2m$js:\\033[0m $(du -h --apparent-size ._ | sed -e 's/[[:space:]]*._//')\"; rm ._; done",
@@ -42,34 +59,39 @@
"license": "APACHE-2.0",
"dependencies": {
"@ungap/with-resolvers": "^0.1.0",
"@webreflection/idb-map": "^0.3.1",
"basic-devtools": "^0.1.6",
"polyscript": "^0.12.10",
"polyscript": "^0.15.12",
"sabayon": "^0.5.2",
"sticky-module": "^0.1.1",
"to-json-callback": "^0.1.1",
"type-checked-collections": "^0.1.7"
},
"devDependencies": {
"@codemirror/commands": "^6.5.0",
"@codemirror/commands": "^6.6.2",
"@codemirror/lang-python": "^6.1.6",
"@codemirror/language": "^6.10.1",
"@codemirror/language": "^6.10.3",
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.26.3",
"@playwright/test": "^1.44.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@codemirror/view": "^6.34.1",
"@playwright/test": "1.45.3",
"@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@webreflection/toml-j0.4": "^1.1.3",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0",
"bun": "^1.1.8",
"chokidar": "^3.6.0",
"bun": "^1.1.29",
"chokidar": "^4.0.1",
"codedent": "^0.1.2",
"codemirror": "^6.0.1",
"eslint": "^9.2.0",
"rollup": "^4.17.2",
"eslint": "^9.11.1",
"flatted": "^3.3.1",
"rollup": "^4.22.5",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-string": "^3.0.0",
"static-handler": "^0.4.3",
"typescript": "^5.4.5",
"static-handler": "^0.5.3",
"string-width": "^7.2.0",
"typescript": "^5.6.2",
"xterm": "^5.3.0",
"xterm-readline": "^1.1.1"
},

View File

@@ -0,0 +1,73 @@
const { join } = require("node:path");
const { lstatSync, readdirSync, writeFileSync } = require("node:fs");
// folders to not consider while crawling
const EXCLUDE_DIR = new Set(["ws"]);
const TEST_DIR = join(__dirname, "..", "tests");
const TEST_INDEX = join(TEST_DIR, "index.html");
const crawl = (path, tree = {}) => {
for (const file of readdirSync(path)) {
const current = join(path, file);
if (current === TEST_INDEX) continue;
if (lstatSync(current).isDirectory()) {
if (EXCLUDE_DIR.has(file)) continue;
const sub = {};
tree[file] = sub;
crawl(current, sub);
if (!Reflect.ownKeys(sub).length) {
delete tree[file];
}
} else if (file.endsWith(".html")) {
const name = file === "index.html" ? "." : file.slice(0, -5);
tree[name] = current.replace(TEST_DIR, "");
}
}
return tree;
};
const createList = (tree) => {
const ul = ["<ul>"];
for (const [key, value] of Object.entries(tree)) {
ul.push("<li>");
if (typeof value === "string") {
ul.push(`<a href=".${value}">${key}<small>.html</small></a>`);
} else {
if ("." in value) {
ul.push(`<strong><a href=".${value["."]}">${key}</a></strong>`);
delete value["."];
} else {
ul.push(`<strong><span>${key}</span></strong>`);
}
if (Reflect.ownKeys(value).length) ul.push(createList(value));
}
ul.push("</li>");
}
ul.push("</ul>");
return ul.join("");
};
writeFileSync(
TEST_INDEX,
`<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PyScript tests</title>
<style>
body { font-family: sans-serif; }
a {
display: block;
transition: opacity .3s;
}
a, span { opacity: .7; }
a:hover { opacity: 1; }
</style>
</head>
<body>${createList(crawl(TEST_DIR))}</body>
</html>
`,
);

View File

@@ -40,4 +40,17 @@ export default [
warn(warning);
},
},
{
input: "./src/storage.js",
plugins: plugins.concat(
process.env.NO_MIN
? [nodeResolve(), commonjs()]
: [nodeResolve(), commonjs(), terser()],
),
output: {
esModule: true,
dir: "./dist",
sourcemap: true,
},
},
];

17
core/rollup/flatted.cjs Normal file
View File

@@ -0,0 +1,17 @@
const { writeFileSync, readFileSync } = require("node:fs");
const { join } = require("node:path");
const flatted = "# https://www.npmjs.com/package/flatted\n\n";
const source = join(
__dirname,
"..",
"node_modules",
"flatted",
"python",
"flatted.py",
);
const dest = join(__dirname, "..", "src", "stdlib", "pyscript", "flatted.py");
const clear = (str) => String(str).replace(/^#.*/gm, "").trimStart();
writeFileSync(dest, flatted + clear(readFileSync(source)));

View File

@@ -13,7 +13,12 @@ for (const file of readdirSync(join(__dirname, "..", "src", "plugins"))) {
plugins.push(
// this comment is needed to avoid bundlers eagerly embedding lazy
// dependencies, causing all sort of issues once in production
` ${key}: () => import(/* webpackIgnore: true */ ${value}),`,
// ⚠️ THIS HAS TO BE LIKE THIS or prettier changes it every single time
` ${key}: () =>
import(
/* webpackIgnore: true */
${value}
),`,
);
}
}

66
core/rollup/stdlib.cjs Normal file
View File

@@ -0,0 +1,66 @@
const {
readdirSync,
readFileSync,
statSync,
writeFileSync,
} = require("node:fs");
const { spawnSync } = require("node:child_process");
const { join } = require("node:path");
const dedent = require("codedent");
const crawl = (path, json) => {
for (const file of readdirSync(path)) {
const full = join(path, file);
if (/\.py$/.test(file)) {
if (process.env.NO_MIN) json[file] = readFileSync(full).toString();
else {
try {
const {
output: [error, result],
} = spawnSync("pyminify", [
"--remove-literal-statements",
full,
]);
if (error) {
console.error(error);
process.exit(1);
}
json[file] = result.toString();
} catch (error) {
console.error(error);
console.log(
dedent(`
\x1b[1m⚠ is your env activated?\x1b[0m
\x1b[2mYou need a Python env to run \x1b[0mpyminify\x1b[2m.\x1b[0m
\x1b[2mTo do so, you can try the following:\x1b[0m
python -m venv env
source env/bin/activate
pip install --upgrade pip
pip install --ignore-requires-python python-minifier
pip install setuptools
\x1b[2mand you can then try \x1b[0mnpm run build\x1b[2m again.\x1b[0m
`),
);
process.exit(1);
}
}
} else if (statSync(full).isDirectory() && !file.endsWith("_"))
crawl(full, (json[file] = {}));
}
};
const json = {};
crawl(join(__dirname, "..", "src", "stdlib"), json);
writeFileSync(
join(__dirname, "..", "src", "stdlib", "pyscript.js"),
`// ⚠️ This file is an artifact: DO NOT MODIFY\nexport default ${JSON.stringify(
json,
null,
" ",
)};\n`,
);

3
core/src/3rd-party/toml.js vendored Normal file

File diff suppressed because one or more lines are too long

7
core/src/3rd-party/xterm-readline.js vendored Normal file

File diff suppressed because one or more lines are too long

7
core/src/3rd-party/xterm.css vendored Normal file
View File

@@ -0,0 +1,7 @@
/**
* Minified by jsDelivr using clean-css v5.3.2.
* Original file: /npm/xterm@5.3.0/css/xterm.css
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}

7
core/src/3rd-party/xterm.js vendored Normal file

File diff suppressed because one or more lines are too long

7
core/src/3rd-party/xterm_addon-fit.js vendored Normal file
View File

@@ -0,0 +1,7 @@
/**
* Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
* Original file: /npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
var e,t,r={exports:{}};self;var s=r.exports=(e=t={},Object.defineProperty(e,"__esModule",{value:!0}),e.FitAddon=void 0,e.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,s=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(s.getPropertyValue("height")),o=Math.max(0,parseInt(s.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=i-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=o-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}},t),i=r.exports.FitAddon,o=r.exports.__esModule;export{i as FitAddon,o as __esModule,s as default};

View File

@@ -0,0 +1,7 @@
/**
* Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
* Original file: /npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
var e={exports:{}};self;var t=e.exports=(()=>{var e={6:(e,t)=>{function r(e){try{const t=new URL(e),r=t.password&&t.username?`${t.protocol}//${t.username}:${t.password}@${t.host}`:t.username?`${t.protocol}//${t.username}@${t.host}`:`${t.protocol}//${t.host}`;return e.toLocaleLowerCase().startsWith(r.toLocaleLowerCase())}catch(e){return!1}}Object.defineProperty(t,"__esModule",{value:!0}),t.LinkComputer=t.WebLinkProvider=void 0,t.WebLinkProvider=class{constructor(e,t,r,n={}){this._terminal=e,this._regex=t,this._handler=r,this._options=n}provideLinks(e,t){const r=n.computeLink(e,this._regex,this._terminal,this._handler);t(this._addCallbacks(r))}_addCallbacks(e){return e.map((e=>(e.leave=this._options.leave,e.hover=(t,r)=>{if(this._options.hover){const{range:n}=e;this._options.hover(t,r,n)}},e)))}};class n{static computeLink(e,t,o,s){const i=new RegExp(t.source,(t.flags||"")+"g"),[a,l]=n._getWindowedLineStrings(e-1,o),c=a.join("");let d;const p=[];for(;d=i.exec(c);){const e=d[0];if(!r(e))continue;const[t,i]=n._mapStrIdx(o,l,0,d.index),[a,c]=n._mapStrIdx(o,t,i,e.length);if(-1===t||-1===i||-1===a||-1===c)continue;const h={start:{x:i+1,y:t+1},end:{x:c,y:a+1}};p.push({range:h,text:e,activate:s})}return p}static _getWindowedLineStrings(e,t){let r,n=e,o=e,s=0,i="";const a=[];if(r=t.buffer.active.getLine(e)){const e=r.translateToString(!0);if(r.isWrapped&&" "!==e[0]){for(s=0;(r=t.buffer.active.getLine(--n))&&s<2048&&(i=r.translateToString(!0),s+=i.length,a.push(i),r.isWrapped&&-1===i.indexOf(" ")););a.reverse()}for(a.push(e),s=0;(r=t.buffer.active.getLine(++o))&&r.isWrapped&&s<2048&&(i=r.translateToString(!0),s+=i.length,a.push(i),-1===i.indexOf(" ")););}return[a,n]}static _mapStrIdx(e,t,r,n){const o=e.buffer.active,s=o.getNullCell();let i=r;for(;n;){const e=o.getLine(t);if(!e)return[-1,-1];for(let r=i;r<e.length;++r){e.getCell(r,s);const i=s.getChars();if(s.getWidth()&&(n-=i.length||1,r===e.length-1&&""===i)){const e=o.getLine(t+1);e&&e.isWrapped&&(e.getCell(0,s),2===s.getWidth()&&(n+=1))}if(n<0)return[t,r]}t++,i=0}return[t,i]}}t.LinkComputer=n}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,r),s.exports}var n={};return(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.WebLinksAddon=void 0;const t=r(6),o=/(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/;function s(e,t){const r=window.open();if(r){try{r.opener=null}catch{}r.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}e.WebLinksAddon=class{constructor(e=s,t={}){this._handler=e,this._options=t}activate(e){this._terminal=e;const r=this._options,n=r.urlRegex||o;this._linkProvider=this._terminal.registerLinkProvider(new t.WebLinkProvider(this._terminal,n,this._handler,r))}dispose(){this._linkProvider?.dispose()}}})(),n})(),r=e.exports.WebLinksAddon,n=e.exports.__esModule;export{r as WebLinksAddon,n as __esModule,t as default};

View File

@@ -45,6 +45,8 @@ const configDetails = async (config, type) => {
const conflictError = (reason) => new Error(`(${CONFLICTING_CODE}): ${reason}`);
const relative_url = (url, base = location.href) => new URL(url, base).href;
const syntaxError = (type, url, { message }) => {
let str = `(${BAD_CONFIG}): Invalid ${type}`;
if (url) str += ` @ ${url}`;
@@ -108,7 +110,7 @@ for (const [TYPE] of TYPES) {
if (!error && config) {
try {
const { json, toml, text, url } = await configDetails(config, type);
if (url) configURL = new URL(url, location.href).href;
if (url) configURL = relative_url(url);
config = text;
if (json || type === "json") {
try {
@@ -153,4 +155,4 @@ for (const [TYPE] of TYPES) {
configs.set(TYPE, { config: parsed, configURL, plugins, error });
}
export default configs;
export { configs, relative_url };

75
core/src/core.css Normal file
View File

@@ -0,0 +1,75 @@
py-script,
py-config,
mpy-script,
mpy-config {
display: none;
}
/* PyEditor */
.py-editor-box,
.mpy-editor-box {
padding: 0.5rem;
}
.py-editor-input,
.mpy-editor-input {
position: relative;
}
.py-editor-box::before,
.mpy-editor-box::before {
content: attr(data-env);
display: block;
font-size: x-small;
text-align: end;
}
.py-editor-output,
.mpy-editor-output {
white-space: pre;
}
.py-editor-run-button,
.mpy-editor-run-button {
position: absolute;
right: 0.5rem;
bottom: 0.5rem;
opacity: 0;
transition: opacity 0.25s;
z-index: 1;
}
.py-editor-box:hover .py-editor-run-button,
.mpy-editor-box:hover .mpy-editor-run-button,
.py-editor-run-button:focus,
.py-editor-run-button:disabled,
.mpy-editor-run-button:focus,
.mpy-editor-run-button:disabled {
opacity: 1;
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}
.py-editor-run-button:disabled > *,
.mpy-editor-run-button:disabled > * {
display: none; /* hide all the child elements of the run button when it is disabled */
}
.py-editor-run-button:disabled,
.mpy-editor-run-button:disabled {
border-width: 0;
}
.py-editor-run-button:disabled::before,
.mpy-editor-run-button:disabled::before {
content: "";
box-sizing: border-box;
position: absolute;
top: 100%;
left: 100%;
width: 20px;
height: 20px;
margin-top: -23px; /* hardcoded value to center the spinner on the run button */
margin-left: -26px; /* hardcoded value to center the spinner on the run button */
border-radius: 50%;
border: 2px solid #aaa;
border-top-color: #000;
background-color: #fff;
animation: spinner 0.6s linear infinite;
}

View File

@@ -12,6 +12,7 @@ import {
define,
defineProperty,
dispatch,
isSync,
queryTarget,
unescape,
whenDefined,
@@ -19,15 +20,22 @@ import {
import "./all-done.js";
import TYPES from "./types.js";
import configs from "./config.js";
import { configs, relative_url } from "./config.js";
import sync from "./sync.js";
import bootstrapNodeAndPlugins from "./plugins-helper.js";
import { ErrorCode } from "./exceptions.js";
import { robustFetch as fetch, getText } from "./fetch.js";
import { hooks, main, worker, codeFor, createFunction } from "./hooks.js";
import {
hooks,
main,
worker,
codeFor,
createFunction,
inputFailure,
} from "./hooks.js";
import { stdlib, optional } from "./stdlib.js";
export { stdlib, optional };
export { stdlib, optional, inputFailure };
// generic helper to disambiguate between custom element and script
const isScript = ({ tagName }) => tagName === "SCRIPT";
@@ -77,6 +85,7 @@ const [
export {
TYPES,
relative_url,
exportedPyWorker as PyWorker,
exportedMPWorker as MPWorker,
exportedHooks as hooks,
@@ -85,7 +94,7 @@ export {
};
export const offline_interpreter = (config) =>
config?.interpreter && new URL(config.interpreter, location.href).href;
config?.interpreter && relative_url(config.interpreter);
const hooked = new Map();
@@ -150,6 +159,7 @@ for (const [TYPE, interpreter] of TYPES) {
// enrich the Python env with some JS utility for main
interpreter.registerJsModule("_pyscript", {
PyWorker,
js_import: (...urls) => Promise.all(urls.map((url) => import(url))),
get target() {
return isScript(currentElement)
? currentElement.target.id
@@ -193,15 +203,13 @@ for (const [TYPE, interpreter] of TYPES) {
}
if (isScript(element)) {
const {
attributes: { async: isAsync, target },
} = element;
const hasTarget = !!target?.value;
const show = hasTarget
? queryTarget(element, target.value)
const isAsync = !isSync(element);
const target = element.getAttribute("target");
const show = target
? queryTarget(element, target)
: document.createElement("script-py");
if (!hasTarget) {
if (!target) {
const { head, body } = document;
if (head.contains(element)) body.append(show);
else element.after(show);
@@ -322,7 +330,7 @@ for (const [TYPE, interpreter] of TYPES) {
async connectedCallback() {
if (!this.executed) {
this.executed = true;
const isAsync = this.hasAttribute("async");
const isAsync = !isSync(this);
const { io, run, runAsync } = await this._wrap
.promise;
this.srcCode = await fetchSource(

View File

@@ -46,7 +46,7 @@ export const createFunction = (self, name) => {
const SetFunction = typedSet({ typeof: "function" });
const SetString = typedSet({ typeof: "string" });
const inputFailure = `
export const inputFailure = `
import builtins
def input(prompt=""):
raise Exception("\\n ".join([

23
core/src/plugins.js Normal file
View File

@@ -0,0 +1,23 @@
// ⚠️ This file is an artifact: DO NOT MODIFY
export default {
["deprecations-manager"]: () =>
import(
/* webpackIgnore: true */
"./plugins/deprecations-manager.js"
),
error: () =>
import(
/* webpackIgnore: true */
"./plugins/error.js"
),
["py-editor"]: () =>
import(
/* webpackIgnore: true */
"./plugins/py-editor.js"
),
["py-terminal"]: () =>
import(
/* webpackIgnore: true */
"./plugins/py-terminal.js"
),
};

View File

@@ -1,6 +1,7 @@
// PyScript py-editor plugin
import { Hook, XWorker, dedent, defineProperties } from "polyscript/exports";
import { TYPES, offline_interpreter, stdlib } from "../core.js";
import { TYPES, offline_interpreter, relative_url, stdlib } from "../core.js";
import { notify } from "./error.js";
const RUN_BUTTON = `<svg style="height:20px;width:20px;vertical-align:-.125em;transform-origin:center;overflow:visible;color:green" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>`;
@@ -23,6 +24,11 @@ const hooks = {
},
};
const validate = (config, result) => {
if (typeof result === "boolean") throw `Invalid source: ${config}`;
return result;
};
async function execute({ currentTarget }) {
const { env, pySrc, outDiv } = this;
const hasRunButton = !!currentTarget;
@@ -34,15 +40,37 @@ async function execute({ currentTarget }) {
if (!envs.has(env)) {
const srcLink = URL.createObjectURL(new Blob([""]));
const details = { type: this.interpreter };
const details = {
type: this.interpreter,
serviceWorker: this.serviceWorker,
};
const { config } = this;
if (config) {
details.configURL = config;
const { parse } = config.endsWith(".toml")
? await import(/* webpackIgnore: true */ "../3rd-party/toml.js")
: JSON;
details.config = parse(await fetch(config).then((r) => r.text()));
details.version = offline_interpreter(details.config);
// verify that config can be parsed and used
try {
details.configURL = relative_url(config);
if (config.endsWith(".toml")) {
const [{ parse }, toml] = await Promise.all([
import(
/* webpackIgnore: true */ "../3rd-party/toml.js"
),
fetch(config).then((r) => r.ok && r.text()),
]);
details.config = parse(validate(config, toml));
} else if (config.endsWith(".json")) {
const json = await fetch(config).then(
(r) => r.ok && r.json(),
);
details.config = validate(config, json);
} else {
details.configURL = relative_url("./config.txt");
details.config = JSON.parse(config);
}
details.version = offline_interpreter(details.config);
} catch (error) {
notify(error);
return;
}
} else {
details.config = {};
}
@@ -63,9 +91,12 @@ async function execute({ currentTarget }) {
return envs.get(env).then((xworker) => {
xworker.onerror = ({ error }) => {
if (hasRunButton) {
outDiv.innerHTML += `<span style='color:red'>${
error.message || error
}</span>\n`;
outDiv.insertAdjacentHTML(
"beforeend",
`<span style='color:red'>${
error.message || error
}</span>\n`,
);
}
console.error(error);
};
@@ -76,31 +107,41 @@ async function execute({ currentTarget }) {
const { sync } = xworker;
sync.write = (str) => {
if (hasRunButton) outDiv.innerText += `${str}\n`;
else console.log(str);
};
sync.writeErr = (str) => {
if (hasRunButton) {
outDiv.innerHTML += `<span style='color:red'>${str}</span>\n`;
outDiv.insertAdjacentHTML(
"beforeend",
`<span style='color:red'>${str}</span>\n`,
);
} else {
notify(str);
console.error(str);
}
};
sync.runAsync(pySrc).then(enable, enable);
});
}
const makeRunButton = (listener, type) => {
const makeRunButton = (handler, type) => {
const runButton = document.createElement("button");
runButton.className = `absolute ${type}-editor-run-button`;
runButton.innerHTML = RUN_BUTTON;
runButton.setAttribute("aria-label", "Python Script Run Button");
runButton.addEventListener("click", listener);
runButton.addEventListener("click", async (event) => {
runButton.blur();
await handler.handleEvent(event);
});
return runButton;
};
const makeEditorDiv = (listener, type) => {
const makeEditorDiv = (handler, type) => {
const editorDiv = document.createElement("div");
editorDiv.className = `${type}-editor-input`;
editorDiv.setAttribute("aria-label", "Python Script Area");
const runButton = makeRunButton(listener, type);
const runButton = makeRunButton(handler, type);
const editorShadowContainer = document.createElement("div");
// avoid outer elements intercepting key events (reveal as example)
@@ -120,15 +161,15 @@ const makeOutDiv = (type) => {
return outDiv;
};
const makeBoxDiv = (listener, type) => {
const makeBoxDiv = (handler, type) => {
const boxDiv = document.createElement("div");
boxDiv.className = `${type}-editor-box`;
const editorDiv = makeEditorDiv(listener, type);
const editorDiv = makeEditorDiv(handler, type);
const outDiv = makeOutDiv(type);
boxDiv.append(editorDiv, outDiv);
return [boxDiv, outDiv];
return [boxDiv, outDiv, editorDiv.querySelector("button")];
};
const init = async (script, type, interpreter) => {
@@ -138,7 +179,7 @@ const init = async (script, type, interpreter) => {
{ python },
{ indentUnit },
{ keymap },
{ defaultKeymap },
{ defaultKeymap, indentWithTab },
] = await Promise.all([
import(/* webpackIgnore: true */ "../3rd-party/codemirror.js"),
import(/* webpackIgnore: true */ "../3rd-party/codemirror_state.js"),
@@ -152,8 +193,17 @@ const init = async (script, type, interpreter) => {
let isSetup = script.hasAttribute("setup");
const hasConfig = script.hasAttribute("config");
const serviceWorker = script.getAttribute("service-worker");
const env = `${interpreter}-${script.getAttribute("env") || getID(type)}`;
// helps preventing too lazy ServiceWorker initialization on button run
if (serviceWorker) {
new XWorker("data:application/javascript,postMessage(0)", {
type: "dummy",
serviceWorker,
}).onmessage = ({ target }) => target.terminate();
}
if (hasConfig && configs.has(env)) {
throw new SyntaxError(
configs.get(env)
@@ -164,15 +214,29 @@ const init = async (script, type, interpreter) => {
configs.set(env, hasConfig);
let source = script.src
? await fetch(script.src).then((b) => b.text())
: script.textContent;
let source = script.textContent;
// verify the src points to a valid file that can be parsed
const { src } = script;
if (src) {
try {
source = validate(
src,
await fetch(src).then((b) => b.ok && b.text()),
);
} catch (error) {
notify(error);
return;
}
}
const context = {
// allow the listener to be overridden at distance
handleEvent: execute,
serviceWorker,
interpreter,
env,
config:
hasConfig &&
new URL(script.getAttribute("config"), location.href).href,
config: hasConfig && script.getAttribute("config"),
get pySrc() {
return isSetup ? source : editor.state.doc.toString();
},
@@ -184,13 +248,53 @@ const init = async (script, type, interpreter) => {
let target;
defineProperties(script, {
target: { get: () => target },
handleEvent: {
get: () => context.handleEvent,
set: (callback) => {
// do not bother with logic if it was set back as its original handler
if (callback === execute) context.handleEvent = execute;
// in every other case be sure that if the listener override returned
// `false` nothing happens, otherwise keep doing what it always did
else {
context.handleEvent = async (event) => {
// trap the currentTarget ASAP (if any)
// otherwise it gets lost asynchronously
const { currentTarget } = event;
// augment a code snapshot before invoking the override
defineProperties(event, {
code: { value: context.pySrc },
});
// avoid executing the default handler if the override returned `false`
if ((await callback(event)) !== false)
await execute.call(context, { currentTarget });
};
}
},
},
code: {
get: () => context.pySrc,
set: (insert) => {
if (isSetup) return;
editor.update([
editor.state.update({
changes: {
from: 0,
to: editor.state.doc.length,
insert,
},
}),
]);
},
},
process: {
/**
* Simulate a setup node overriding the source to evaluate.
* @param {string} code the Python code to evaluate.
* @param {boolean} asRunButtonAction invoke the `Run` button handler.
* @returns {Promise<...>} fulfill once code has been evaluated.
*/
value(code) {
value(code, asRunButtonAction = false) {
if (asRunButtonAction) return listener();
const wasSetup = isSetup;
const wasSource = source;
isSetup = true;
@@ -199,21 +303,21 @@ const init = async (script, type, interpreter) => {
isSetup = wasSetup;
source = wasSource;
};
return execute
.call(context, { currentTarget: null })
return context
.handleEvent({ currentTarget: null })
.then(restore, restore);
},
},
});
const notify = () => {
const notifyEditor = () => {
const event = new Event(`${type}-editor`, { bubbles: true });
script.dispatchEvent(event);
};
if (isSetup) {
await execute.call(context, { currentTarget: null });
notify();
await context.handleEvent({ currentTarget: null });
notifyEditor();
return;
}
@@ -235,8 +339,7 @@ const init = async (script, type, interpreter) => {
if (!target.hasAttribute("root")) target.setAttribute("root", target.id);
// @see https://github.com/JeffersGlass/mkdocs-pyscript/blob/main/mkdocs_pyscript/js/makeblocks.js
const listener = execute.bind(context);
const [boxDiv, outDiv] = makeBoxDiv(listener, type);
const [boxDiv, outDiv, runButton] = makeBoxDiv(context, type);
boxDiv.dataset.env = script.hasAttribute("env") ? env : interpreter;
const inputChild = boxDiv.querySelector(`.${type}-editor-input > div`);
@@ -249,8 +352,9 @@ const init = async (script, type, interpreter) => {
const doc = dedent(script.textContent).trim();
// preserve user indentation, if any
const indentation = /^(\s+)/m.test(doc) ? RegExp.$1 : " ";
const indentation = /^([ \t]+)/m.test(doc) ? RegExp.$1 : " ";
const listener = () => runButton.click();
const editor = new EditorView({
extensions: [
indentUnit.of(indentation),
@@ -260,15 +364,19 @@ const init = async (script, type, interpreter) => {
{ key: "Ctrl-Enter", run: listener, preventDefault: true },
{ key: "Cmd-Enter", run: listener, preventDefault: true },
{ key: "Shift-Enter", run: listener, preventDefault: true },
// @see https://codemirror.net/examples/tab/
indentWithTab,
]),
basicSetup,
],
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
parent,
doc,
});
editor.focus();
notify();
notifyEditor();
};
// avoid too greedy MutationObserver operations at distance

View File

@@ -0,0 +1,60 @@
// PyScript py-terminal plugin
import { TYPES, relative_url } from "../core.js";
import { notify } from "./error.js";
import { customObserver } from "polyscript/exports";
// will contain all valid selectors
const SELECTORS = [];
// avoid processing same elements twice
const processed = new WeakSet();
// show the error on main and
// stops the module from keep executing
const notifyAndThrow = (message) => {
notify(message);
throw new Error(message);
};
const onceOnMain = ({ attributes: { worker } }) => !worker;
let addStyle = true;
for (const type of TYPES.keys()) {
const selector = `script[type="${type}"][terminal],${type}-script[terminal]`;
SELECTORS.push(selector);
customObserver.set(selector, async (element) => {
// we currently support only one terminal on main as in "classic"
const terminals = document.querySelectorAll(SELECTORS.join(","));
if ([].filter.call(terminals, onceOnMain).length > 1)
notifyAndThrow("You can use at most 1 main terminal");
// import styles lazily
if (addStyle) {
addStyle = false;
document.head.append(
Object.assign(document.createElement("link"), {
rel: "stylesheet",
href: relative_url("./xterm.css", import.meta.url),
}),
);
}
if (processed.has(element)) return;
processed.add(element);
const bootstrap = (module) => module.default(element);
// we can't be smart with template literals for the dynamic import
// or bundlers are incapable of producing multiple files around
if (type === "mpy") {
await import(/* webpackIgnore: true */ "./py-terminal/mpy.js").then(
bootstrap,
);
} else {
await import(/* webpackIgnore: true */ "./py-terminal/py.js").then(
bootstrap,
);
}
});
}

View File

@@ -0,0 +1,252 @@
// PyScript pyodide terminal plugin
import { hooks, inputFailure } from "../../core.js";
import { defineProperties } from "polyscript/exports";
const bootstrapped = new WeakSet();
// this callback will be serialized as string and it never needs
// to be invoked multiple times. Each xworker here is bootstrapped
// only once thanks to the `sync.is_pyterminal()` check.
const workerReady = ({ interpreter, io, run, type }, { sync }) => {
if (type !== "mpy" || !sync.is_pyterminal()) return;
const { pyterminal_ready, pyterminal_read, pyterminal_write } = sync;
interpreter.registerJsModule("_pyscript_input", {
input: pyterminal_read,
});
run(
[
"from _pyscript_input import input",
"from polyscript import currentScript as _",
"__terminal__ = _.terminal",
"del _",
].join(";"),
);
const missingReturn = new Uint8Array([13]);
io.stdout = (buffer) => {
if (buffer[0] === 10) pyterminal_write(missingReturn);
pyterminal_write(buffer);
};
io.stderr = (error) => {
pyterminal_write(String(error.message || error));
};
// tiny shim of the code module with only interact
// to bootstrap a REPL like environment
interpreter.registerJsModule("code", {
interact() {
const encoder = new TextEncoderStream();
encoder.readable.pipeTo(
new WritableStream({
write(buffer) {
for (const c of buffer) interpreter.replProcessChar(c);
},
}),
);
const writer = encoder.writable.getWriter();
sync.pyterminal_stream_write = (buffer) => writer.write(buffer);
interpreter.replInit();
},
});
pyterminal_ready();
};
export default async (element) => {
// lazy load these only when a valid terminal is found
const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = await Promise.all([
import(/* webpackIgnore: true */ "../../3rd-party/xterm.js"),
import(/* webpackIgnore: true */ "../../3rd-party/xterm_addon-fit.js"),
import(
/* webpackIgnore: true */ "../../3rd-party/xterm_addon-web-links.js"
),
]);
const terminalOptions = {
disableStdin: false,
cursorBlink: true,
cursorStyle: "block",
};
let stream;
// common main thread initialization for both worker
// or main case, bootstrapping the terminal on its target
const init = () => {
let target = element;
const selector = element.getAttribute("target");
if (selector) {
target =
document.getElementById(selector) ||
document.querySelector(selector);
if (!target) throw new Error(`Unknown target ${selector}`);
} else {
target = document.createElement("py-terminal");
target.style.display = "block";
element.after(target);
}
const terminal = new Terminal({
theme: {
background: "#191A19",
foreground: "#F5F2E7",
},
...terminalOptions,
});
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);
terminal.loadAddon(new WebLinksAddon());
terminal.open(target);
fitAddon.fit();
terminal.focus();
defineProperties(element, {
terminal: { value: terminal },
process: {
value: async (code) => {
for (const line of code.split(/(?:\r\n|\r|\n)/)) {
await stream.write(`${line}\r`);
}
},
},
});
return terminal;
};
// branch logic for the worker
if (element.hasAttribute("worker")) {
// add a hook on the main thread to setup all sync helpers
// also bootstrapping the XTerm target on main *BUT* ...
hooks.main.onWorker.add(function worker(_, xworker) {
// ... as multiple workers will add multiple callbacks
// be sure no xworker is ever initialized twice!
if (bootstrapped.has(xworker)) return;
bootstrapped.add(xworker);
// still cleanup this callback for future scripts/workers
hooks.main.onWorker.delete(worker);
const terminal = init();
const { sync } = xworker;
// handle the read mode on input
let promisedChunks = null;
let readChunks = "";
sync.is_pyterminal = () => true;
// put the terminal in a read-only state
// frees the worker on \r
sync.pyterminal_read = (buffer) => {
terminal.write(buffer);
promisedChunks = Promise.withResolvers();
return promisedChunks.promise;
};
// write if not reading input
sync.pyterminal_write = (buffer) => {
if (!promisedChunks) terminal.write(buffer);
};
// add the onData terminal listener which forwards to the worker
// everything typed in a queued char-by-char way
sync.pyterminal_ready = () => {
let queue = Promise.resolve();
stream = {
write: (buffer) =>
(queue = queue.then(() =>
sync.pyterminal_stream_write(buffer),
)),
};
terminal.onData((buffer) => {
if (promisedChunks) {
// handle backspace on input
if (buffer === "\x7f") {
// avoid over-greedy backspace
if (readChunks.length) {
readChunks = readChunks.slice(0, -1);
// override previous char position
// put an empty space to clear the char
// move back position again
buffer = "\b \b";
} else buffer = "";
} else readChunks += buffer;
if (buffer) {
terminal.write(buffer);
if (readChunks.endsWith("\r")) {
terminal.write("\n");
promisedChunks.resolve(readChunks.slice(0, -1));
promisedChunks = null;
readChunks = "";
}
}
} else {
stream.write(buffer);
}
});
};
});
// setup remote thread JS/Python code for whenever the
// worker is ready to become a terminal
hooks.worker.onReady.add(workerReady);
} else {
// ⚠️ In an ideal world the inputFailure should never be used on main.
// However, Pyodide still can't compete with MicroPython REPL mode
// so while it's OK to keep that entry on main as default, we need
// to remove it ASAP from `mpy` use cases, otherwise MicroPython would
// also throw whenever an `input(...)` is required / digited.
hooks.main.codeBeforeRun.delete(inputFailure);
// in the main case, just bootstrap XTerm without
// allowing any input as that's not possible / awkward
hooks.main.onReady.add(function main({ interpreter, io, run, type }) {
if (type !== "mpy") return;
hooks.main.onReady.delete(main);
const terminal = init();
const missingReturn = new Uint8Array([13]);
io.stdout = (buffer) => {
if (buffer[0] === 10) terminal.write(missingReturn);
terminal.write(buffer);
};
// expose the __terminal__ one-off reference
globalThis.__py_terminal__ = terminal;
run(
[
"from js import prompt as input",
"from js import __py_terminal__ as __terminal__",
].join(";"),
);
delete globalThis.__py_terminal__;
// NOTE: this is NOT the same as the one within
// the onWorkerReady callback!
interpreter.registerJsModule("code", {
interact() {
const encoder = new TextEncoderStream();
encoder.readable.pipeTo(
new WritableStream({
write(buffer) {
for (const c of buffer)
interpreter.replProcessChar(c);
},
}),
);
stream = encoder.writable.getWriter();
terminal.onData((buffer) => stream.write(buffer));
interpreter.replInit();
},
});
});
}
};

View File

@@ -0,0 +1,179 @@
// PyScript py-terminal plugin
import { hooks } from "../../core.js";
import { defineProperties } from "polyscript/exports";
const bootstrapped = new WeakSet();
// this callback will be serialized as string and it never needs
// to be invoked multiple times. Each xworker here is bootstrapped
// only once thanks to the `sync.is_pyterminal()` check.
const workerReady = ({ interpreter, io, run, type }, { sync }) => {
if (type !== "py" || !sync.is_pyterminal()) return;
run(
[
"from polyscript import currentScript as _",
"__terminal__ = _.terminal",
"del _",
].join(";"),
);
let data = "";
const { pyterminal_read, pyterminal_write } = sync;
const decoder = new TextDecoder();
const generic = {
isatty: false,
write(buffer) {
data = decoder.decode(buffer);
pyterminal_write(data);
return buffer.length;
},
};
io.stderr = (error) => {
pyterminal_write(String(error.message || error));
};
interpreter.setStdout(generic);
interpreter.setStderr(generic);
interpreter.setStdin({
isatty: false,
stdin: () => pyterminal_read(data),
});
};
export default async (element) => {
// lazy load these only when a valid terminal is found
const [{ Terminal }, { Readline }, { FitAddon }, { WebLinksAddon }] =
await Promise.all([
import(/* webpackIgnore: true */ "../../3rd-party/xterm.js"),
import(
/* webpackIgnore: true */ "../../3rd-party/xterm-readline.js"
),
import(
/* webpackIgnore: true */ "../../3rd-party/xterm_addon-fit.js"
),
import(
/* webpackIgnore: true */ "../../3rd-party/xterm_addon-web-links.js"
),
]);
const readline = new Readline();
// common main thread initialization for both worker
// or main case, bootstrapping the terminal on its target
const init = (options) => {
let target = element;
const selector = element.getAttribute("target");
if (selector) {
target =
document.getElementById(selector) ||
document.querySelector(selector);
if (!target) throw new Error(`Unknown target ${selector}`);
} else {
target = document.createElement("py-terminal");
target.style.display = "block";
element.after(target);
}
const terminal = new Terminal({
theme: {
background: "#191A19",
foreground: "#F5F2E7",
},
...options,
});
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);
terminal.loadAddon(readline);
terminal.loadAddon(new WebLinksAddon());
terminal.open(target);
fitAddon.fit();
terminal.focus();
defineProperties(element, {
terminal: { value: terminal },
process: {
value: async (code) => {
for (const line of code.split(/(?:\r\n|\r|\n)/)) {
terminal.paste(`${line}`);
terminal.write("\r\n");
do {
await new Promise((resolve) =>
setTimeout(resolve, 0),
);
} while (!readline.activeRead?.resolve);
readline.activeRead.resolve(line);
}
},
},
});
return terminal;
};
// branch logic for the worker
if (element.hasAttribute("worker")) {
// add a hook on the main thread to setup all sync helpers
// also bootstrapping the XTerm target on main *BUT* ...
hooks.main.onWorker.add(function worker(_, xworker) {
// ... as multiple workers will add multiple callbacks
// be sure no xworker is ever initialized twice!
if (bootstrapped.has(xworker)) return;
bootstrapped.add(xworker);
// still cleanup this callback for future scripts/workers
hooks.main.onWorker.delete(worker);
init({
disableStdin: false,
cursorBlink: true,
cursorStyle: "block",
});
xworker.sync.is_pyterminal = () => true;
xworker.sync.pyterminal_read = readline.read.bind(readline);
xworker.sync.pyterminal_write = readline.write.bind(readline);
});
// setup remote thread JS/Python code for whenever the
// worker is ready to become a terminal
hooks.worker.onReady.add(workerReady);
} else {
// in the main case, just bootstrap XTerm without
// allowing any input as that's not possible / awkward
hooks.main.onReady.add(function main({ interpreter, io, run, type }) {
if (type !== "py") return;
console.warn("py-terminal is read only on main thread");
hooks.main.onReady.delete(main);
// on main, it's easy to trash and clean the current terminal
globalThis.__py_terminal__ = init({
disableStdin: true,
cursorBlink: false,
cursorStyle: "underline",
});
run("from js import __py_terminal__ as __terminal__");
delete globalThis.__py_terminal__;
io.stderr = (error) => {
readline.write(String(error.message || error));
};
let data = "";
const decoder = new TextDecoder();
const generic = {
isatty: false,
write(buffer) {
data = decoder.decode(buffer);
readline.write(data);
return buffer.length;
},
};
interpreter.setStdout(generic);
interpreter.setStderr(generic);
interpreter.setStdin({
isatty: false,
stdin: () => readline.read(data),
});
});
}
};

View File

@@ -37,7 +37,7 @@ const python = [
"_path = None",
];
const ignore = new Ignore(python, "./pyweb");
const ignore = new Ignore(python, "-");
const write = (base, literal) => {
for (const [key, value] of entries(literal)) {

File diff suppressed because one or more lines are too long

View File

@@ -29,6 +29,7 @@
# pyscript.magic_js. This is the blessed way to access them from pyscript,
# as it works transparently in both the main thread and worker cases.
from polyscript import lazy_py_modules as py_import
from pyscript.display import HTML, display
from pyscript.fetch import fetch
from pyscript.magic_js import (
@@ -37,12 +38,17 @@ from pyscript.magic_js import (
config,
current_target,
document,
js_import,
js_modules,
sync,
window,
)
from pyscript.storage import Storage, storage
from pyscript.websocket import WebSocket
if not RUNNING_IN_WORKER:
from pyscript.workers import create_named_worker, workers
try:
from pyscript.event_handling import when
except:

View File

@@ -0,0 +1,76 @@
import inspect
try:
from pyodide.ffi.wrappers import add_event_listener
except ImportError:
def add_event_listener(el, event_type, func):
el.addEventListener(event_type, func)
from pyscript.magic_js import document
def when(event_type=None, selector=None):
"""
Decorates a function and passes py-* events to the decorated function
The events might or not be an argument of the decorated function
"""
def decorator(func):
from pyscript.web import Element, ElementCollection
if isinstance(selector, str):
elements = document.querySelectorAll(selector)
# TODO: This is a hack that will be removed when pyscript becomes a package
# and we can better manage the imports without circular dependencies
elif isinstance(selector, Element):
elements = [selector._dom_element]
elif isinstance(selector, ElementCollection):
elements = [el._dom_element for el in selector]
else:
if isinstance(selector, list):
elements = selector
else:
elements = [selector]
try:
sig = inspect.signature(func)
# Function doesn't receive events
if not sig.parameters:
# Function is async: must be awaited
if inspect.iscoroutinefunction(func):
async def wrapper(*args, **kwargs):
await func()
else:
def wrapper(*args, **kwargs):
func()
else:
wrapper = func
except AttributeError:
# TODO: this is very ugly hack to get micropython working because inspect.signature
# doesn't exist, but we need to actually properly replace inspect.signature.
# It may be actually better to not try any magic for now and raise the error
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except TypeError as e:
if "takes" in str(e) and "positional arguments" in str(e):
return func()
raise
for el in elements:
add_event_listener(el, event_type, wrapper)
return func
return decorator

View File

@@ -0,0 +1,148 @@
# https://www.npmjs.com/package/flatted
import json as _json
class _Known:
def __init__(self):
self.key = []
self.value = []
class _String:
def __init__(self, value):
self.value = value
def _array_keys(value):
keys = []
i = 0
for _ in value:
keys.append(i)
i += 1
return keys
def _object_keys(value):
keys = []
for key in value:
keys.append(key)
return keys
def _is_array(value):
return isinstance(value, list) or isinstance(value, tuple)
def _is_object(value):
return isinstance(value, dict)
def _is_string(value):
return isinstance(value, str)
def _index(known, input, value):
input.append(value)
index = str(len(input) - 1)
known.key.append(value)
known.value.append(index)
return index
def _loop(keys, input, known, output):
for key in keys:
value = output[key]
if isinstance(value, _String):
_ref(key, input[int(value.value)], input, known, output)
return output
def _ref(key, value, input, known, output):
if _is_array(value) and not value in known:
known.append(value)
value = _loop(_array_keys(value), input, known, value)
elif _is_object(value) and not value in known:
known.append(value)
value = _loop(_object_keys(value), input, known, value)
output[key] = value
def _relate(known, input, value):
if _is_string(value) or _is_array(value) or _is_object(value):
try:
return known.value[known.key.index(value)]
except:
return _index(known, input, value)
return value
def _transform(known, input, value):
if _is_array(value):
output = []
for val in value:
output.append(_relate(known, input, val))
return output
if _is_object(value):
obj = {}
for key in value:
obj[key] = _relate(known, input, value[key])
return obj
return value
def _wrap(value):
if _is_string(value):
return _String(value)
if _is_array(value):
i = 0
for val in value:
value[i] = _wrap(val)
i += 1
elif _is_object(value):
for key in value:
value[key] = _wrap(value[key])
return value
def parse(value, *args, **kwargs):
json = _json.loads(value, *args, **kwargs)
wrapped = []
for value in json:
wrapped.append(_wrap(value))
input = []
for value in wrapped:
if isinstance(value, _String):
input.append(value.value)
else:
input.append(value)
value = input[0]
if _is_array(value):
return _loop(_array_keys(value), input, [value], value)
if _is_object(value):
return _loop(_object_keys(value), input, [value], value)
return value
def stringify(value, *args, **kwargs):
known = _Known()
input = []
output = []
i = int(_index(known, input, value))
while i < len(input):
output.append(_transform(known, input, input[i]))
i += 1
return _json.dumps(output, *args, **kwargs)

View File

@@ -10,6 +10,11 @@ RUNNING_IN_WORKER = not hasattr(globalThis, "document")
config = json.loads(globalThis.JSON.stringify(_config))
if "MicroPython" in sys.version:
config["type"] = "mpy"
else:
config["type"] = "py"
# allow `from pyscript.js_modules.xxx import yyy`
class JSModule:
@@ -36,24 +41,21 @@ if RUNNING_IN_WORKER:
)
try:
globalThis.SharedArrayBuffer.new(4)
import js
window = polyscript.xworker.window
document = window.document
js.document = document
# this is the same as js_import on main and it lands modules on main
js_import = window.Function(
"return (...urls) => Promise.all(urls.map((url) => import(url)))"
)()
except:
globalThis.console.debug("SharedArrayBuffer is not available")
# in this scenario none of the utilities would work
# as expected so we better export these as NotSupported
window = NotSupported(
"pyscript.window",
"pyscript.window in workers works only via SharedArrayBuffer",
)
document = NotSupported(
"pyscript.document",
"pyscript.document in workers works only via SharedArrayBuffer",
)
message = "Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer"
globalThis.console.warn(message)
window = NotSupported("pyscript.window", message)
document = NotSupported("pyscript.document", message)
js_import = None
sync = polyscript.xworker.sync
@@ -64,7 +66,7 @@ if RUNNING_IN_WORKER:
else:
import _pyscript
from _pyscript import PyWorker
from _pyscript import PyWorker, js_import
window = globalThis
document = globalThis.document

View File

@@ -0,0 +1,60 @@
from polyscript import storage as _storage
from pyscript.flatted import parse as _parse
from pyscript.flatted import stringify as _stringify
# convert a Python value into an IndexedDB compatible entry
def _to_idb(value):
if value is None:
return _stringify(["null", 0])
if isinstance(value, (bool, float, int, str, list, dict, tuple)):
return _stringify(["generic", value])
if isinstance(value, bytearray):
return _stringify(["bytearray", [v for v in value]])
if isinstance(value, memoryview):
return _stringify(["memoryview", [v for v in value]])
raise TypeError(f"Unexpected value: {value}")
# convert an IndexedDB compatible entry into a Python value
def _from_idb(value):
(
kind,
result,
) = _parse(value)
if kind == "null":
return None
if kind == "generic":
return result
if kind == "bytearray":
return bytearray(result)
if kind == "memoryview":
return memoryview(bytearray(result))
return value
class Storage(dict):
def __init__(self, store):
super().__init__({k: _from_idb(v) for k, v in store.entries()})
self.__store__ = store
def __delitem__(self, attr):
self.__store__.delete(attr)
super().__delitem__(attr)
def __setitem__(self, attr, value):
self.__store__.set(attr, _to_idb(value))
super().__setitem__(attr, value)
def clear(self):
self.__store__.clear()
super().clear()
async def sync(self):
await self.__store__.sync()
async def storage(name="", storage_class=Storage):
if not name:
raise ValueError("The storage name must be defined")
return storage_class(await _storage(f"@pyscript/{name}"))

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,11 @@
import js
from pyscript.ffi import create_proxy
from pyscript.util import as_bytearray
code = "code"
protocols = "protocols"
reason = "reason"
methods = ["onclose", "onerror", "onmessage", "onopen"]
class EventMessage:
@@ -36,18 +38,20 @@ class WebSocket(object):
socket = js.WebSocket.new(url)
object.__setattr__(self, "_ws", socket)
for t in ["onclose", "onerror", "onmessage", "onopen"]:
for t in methods:
if t in kw:
socket[t] = kw[t]
# Pyodide fails at setting socket[t] directly
setattr(socket, t, create_proxy(kw[t]))
def __getattr__(self, attr):
return getattr(self._ws, attr)
def __setattr__(self, attr, value):
if attr == "onmessage":
self._ws[attr] = lambda e: value(EventMessage(e))
if attr in methods:
m = lambda e: value(EventMessage(e))
setattr(self._ws, attr, create_proxy(m))
else:
self._ws[attr] = value
setattr(self._ws, attr, value)
def close(self, **kw):
if code in kw and reason in kw:

View File

@@ -0,0 +1,43 @@
import js as _js
from polyscript import workers as _workers
_get = _js.Reflect.get
def _set(script, name, value=""):
script.setAttribute(name, value)
# this solves an inconsistency between Pyodide and MicroPython
# @see https://github.com/pyscript/pyscript/issues/2106
class _ReadOnlyProxy:
def __getitem__(self, name):
return _get(_workers, name)
def __getattr__(self, name):
return _get(_workers, name)
workers = _ReadOnlyProxy()
async def create_named_worker(src="", name="", config=None, type="py"):
from json import dumps
if not src:
raise ValueError("Named workers require src")
if not name:
raise ValueError("Named workers require a name")
s = _js.document.createElement("script")
s.type = type
s.src = src
_set(s, "worker")
_set(s, "name", name)
if config:
_set(s, "config", isinstance(config, str) and config or dumps(config))
_js.document.body.append(s)
return await workers[name]

72
core/src/storage.js Normal file
View File

@@ -0,0 +1,72 @@
import { ArrayBuffer, TypedArray } from "sabayon/shared";
import IDBMapSync from "@webreflection/idb-map/sync";
import { parse, stringify } from "flatted";
const to_idb = (value) => {
if (value == null) return stringify(["null", 0]);
/* eslint-disable no-fallthrough */
switch (typeof value) {
case "object": {
if (value instanceof TypedArray)
return stringify(["memoryview", [...value]]);
if (value instanceof ArrayBuffer)
return stringify(["bytearray", [...new Uint8Array(value)]]);
}
case "string":
case "number":
case "boolean":
return stringify(["generic", value]);
default:
throw new TypeError(`Unexpected value: ${String(value)}`);
}
};
const from_idb = (value) => {
const [kind, result] = parse(value);
if (kind === "null") return null;
if (kind === "generic") return result;
if (kind === "bytearray") return new Uint8Array(value).buffer;
if (kind === "memoryview") return new Uint8Array(value);
return value;
};
// this export simulate pyscript.storage exposed in the Python world
export const storage = async (name) => {
if (!name) throw new SyntaxError("The storage name must be defined");
const store = new IDBMapSync(`@pyscript/${name}`);
const map = new Map();
await store.sync();
for (const [k, v] of store.entries()) map.set(k, from_idb(v));
const clear = () => {
map.clear();
store.clear();
};
const sync = async () => {
await store.sync();
};
return new Proxy(map, {
ownKeys: (map) => [...map.keys()],
has: (map, name) => map.has(name),
get: (map, name) => {
if (name === "clear") return clear;
if (name === "sync") return sync;
return map.get(name);
},
set: (map, name, value) => {
map.set(name, value);
store.set(name, to_idb(value));
return true;
},
deleteProperty: (map, name) => {
if (map.has(name)) {
map.delete(name);
store.delete(name);
}
return true;
},
});
};

49
core/tests/README.md Normal file
View File

@@ -0,0 +1,49 @@
# PyScript Test Suite
There are three aspects to our test suite. These are reflected in the layout of
the test directory:
1. `python` - contains the Python based test suite to exercise Python code
**within** PyScript. These tests are run four differeng ways to ensure all
combination of MicroPython/Pyodide and main thread/worker contexts are
checked.
2. `javascript` - contains JavaScript tests to exercise PyScript _itself_, in
the browser.
3. `manual` - contains tests to run manually in a browser, due to the complex
nature of the tests.
We use [Playwright](https://playwright.dev/) to automate the running of the
Python and JavaScript test suites. We use
[uPyTest](https://github.com/ntoll/upytest) as a test framework for the Python
test suite. uPyTest is a "PyTest inspired" framework for running tests in the
browser on both MicroPython and Pyodide.
The automated (Playwright) tests are specified in the `integration.spec.js`
file in this directory.
All automatic tests live in either the `python` or `javascript` folders. All
the tests in these folder are run by CI or locally run by `make test` in the
root of this project. Alternatively, run `npm run test:integration` in the
PyScript source directory.
Similarly, some tests can only be run manually (due to their nature or
underlying complexity). These are in the `manual` directory and are in the form
of separate directories (each containing an `index.html`) or individual `*.html`
files to which you point your browser. Each separate test may exercise
JavaScript or Python code (or both), and the context for each separate test is
kept carefully isolated.
Some rules of thumb:
* We don't test upstream projects: we assume they have their own test suites,
and if we find bugs, we file an issue upstream with an example of how to
recreate the problem.
* We don't test browser functionality, we just have to trust that browsers work
as advertised. Once again, if we find an issue, we report upstream.
* All test cases should include commentary describing the **intent** and
context of the test.
* Tests in Python use [uPyTest](https://github.com/ntoll/upytest) (see the
README for documentation), an "inspired by PyTest" test framework that works
with both MicroPython and Pyodide in the browser. This means that all
Python tests should work with both interpreters.
* Tests in JavaScript... (Andrea to explain). ;-)

18
core/tests/index.html Normal file
View File

@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PyScript tests</title>
<style>
body { font-family: sans-serif; }
a {
display: block;
transition: opacity .3s;
}
a, span { opacity: .7; }
a:hover { opacity: 1; }
</style>
</head>
<body><ul><li><strong><a href="./config/index.html">config</a></strong><ul><li><a href="./config/ambiguous-config.html">ambiguous-config<small>.html</small></a></li><li><a href="./config/same-config.html">same-config<small>.html</small></a></li><li><a href="./config/too-many-config.html">too-many-config<small>.html</small></a></li><li><a href="./config/too-many-py-config.html">too-many-py-config<small>.html</small></a></li></ul></li><li><strong><a href="./issue-7015/index.html">issue-7015</a></strong></li><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><a href="./javascript/workers/index.html">workers</a></strong><ul><li><a href="./javascript/workers/named.html">named<small>.html</small></a></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./piratical/index.html">piratical</a></strong></li><li><strong><a href="./py-editor/index.html">py-editor</a></strong><ul><li><a href="./py-editor/issue-2056.html">issue-2056<small>.html</small></a></li><li><a href="./py-editor/service-worker.html">service-worker<small>.html</small></a></li></ul></li><li><strong><a href="./py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li><li><strong><a href="./service-worker/index.html">service-worker</a></strong></li></ul></body>
</html>

View File

@@ -0,0 +1,4 @@
packages = [
"https://cdn.holoviz.org/panel/wheels/bokeh-3.5.0-py3-none-any.whl",
"https://cdn.holoviz.org/panel/1.5.0-b.2/dist/wheels/panel-1.5.0b2-py3-none-any.whl"
]

View File

@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../../dist/core.css">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.5.0.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.0.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@holoviz/panel@1.5.0-b.2/dist/panel.min.js"></script>
<script type="module" src="../../dist/core.js"></script>
</head>
<body>
<script type="py" src="main.py" config="config.toml" worker></script>
<div id="simple_app"></div>
</body>
</html>

View File

@@ -0,0 +1,12 @@
import panel as pn
pn.extension(sizing_mode="stretch_width")
slider = pn.widgets.FloatSlider(start=0, end=10, name="amplitude")
def callback(new):
return f"Amplitude is: {new}"
pn.Row(slider, pn.bind(callback, slider)).servable(target="simple_app")

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script>
</head>
<body>
<script type="mpy">
from pyscript import window, document, fetch, when
@when('click', '#click')
async def click(event):
text = await fetch(window.location.href).text()
document.getElementById('output').append(text)
document.documentElement.classList.add('ok')
document.getElementById('click').click()
</script>
<button id="click">click</button>
<pre id="output"></pre>
</body>
</html>

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PyScript Next Plugin</title>
<link rel="stylesheet" href="../dist/core.css">
<script type="module" src="../dist/core.js"></script>
<link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script>
<mpy-config src="config-url/config.json"></mpy-config>
<script type="mpy">
from pyscript import config

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script>
</head>
<body>
<script type="mpy">
from pyscript import config, document
if config["type"] is "mpy":
document.documentElement.classList.add("mpy")
</script>
<script type="py">
from pyscript import config, document
if config["type"] is "py":
document.documentElement.classList.add("py")
</script>
</body>
</html>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../dist/core.css">
<link rel="stylesheet" href="../../../dist/core.css">
</head>
<body>
<script type="module">
@@ -18,7 +18,7 @@
document.createElement('script'),
{
type: 'module',
src: '../dist/core.js'
src: '../../../dist/core.js'
}
)
);

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyScript FFI</title>
<link rel="stylesheet" href="../dist/core.css">
<script type="module" src="../dist/core.js"></script>
<link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script>
</head>
<body>
<script type="mpy">

View File

@@ -4,13 +4,13 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PyScript Next Plugin Bug?</title>
<link rel="stylesheet" href="../dist/core.css">
<link rel="stylesheet" href="../../dist/core.css">
<script type="module">
addEventListener('mpy:done', () => {
document.documentElement.classList.add('done');
});
import { hooks } from "../dist/core.js";
import { hooks } from "../../dist/core.js";
// Main
hooks.main.onReady.add((wrap, element) => {
@@ -48,12 +48,12 @@
</script>
</head>
<body>
<script type="mpy" worker>
<script type="mpy" async="false" worker>
from pyscript import document
print("actual code in worker")
document.documentElement.classList.add('worker')
</script>
<script type="mpy">
<script type="mpy" async="false">
print("actual code in main")
</script>
</body>

View File

@@ -0,0 +1,6 @@
const { error } = console;
console.error = (...args) => {
error(...args);
document.documentElement.classList.add('errored');
};

View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../../../dist/core.css">
<script type="module" src="./error.js"></script>
<script type="module" src="../../../dist/core.js"></script>
</head>
<body>
<script type="mpy-editor" setup>
print("Hello Editor")
raise Exception("failure")
</script>
</body>
</html>

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../dist/core.css">
<script type="module">
import { storage } from '../../dist/storage.js';
const store = await storage('js-storage');
store.test = [1, 2, 3].map(String);
await store.sync();
// be sure the store is not empty before bootstrap
import('../../dist/core.js');
</script>
</head>
<body>
<script type="mpy">
from pyscript import storage, document
store = await storage("js-storage")
if ",".join(store["test"]) == "1,2,3":
document.documentElement.classList.add("ok")
</script>
</body>
</html>

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../dist/core.css">
<script type="module" src="../dist/core.js"></script>
<link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script>
</head>
<body>
<mpy-config>

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../../dist/core.css">
<script type="module">
import '../../../dist/core.js';
addEventListener('py:progress', ({ detail }) => {
document.body.append(
detail,
document.createElement('br'),
);
});
</script>
</head>
<body>
<py-config>
packages = ["matplotlib"]
</py-config>
<script type="py" worker>
from pyscript import document
from sys import version
document.body.append(document.createElement('hr'), version)
document.documentElement.classList.add('ok')
</script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More