Skip to content

Commit 50a92ee

Browse files
committed
Updates around latest changes and docs
1 parent 7521c23 commit 50a92ee

File tree

6 files changed

+176
-3
lines changed

6 files changed

+176
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ of this repository).
3232
# example of a simple virtual environment
3333
# creation from the root of this project
3434
python -m venv .
35+
./bin/pip install --upgrade setuptools
3536
./bin/pip install -r requirements.txt
3637
```
3738

docs/faq.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ done"` message is written to the browser's console.
705705

706706
Applications need third party packages and [PyScript can be configured to
707707
automatically install packages for you](user-guide/configuration/#packages).
708-
Yet [packaging can be a complicated beast](#python-packages), so here are some
708+
Yet packaging can be a complicated beast, so here are some
709709
hints for a painless packaging experience with PyScript.
710710

711711
There are essentially four ways in which a third party package can become
@@ -724,9 +724,10 @@ available in PyScript.
724724
3. Reference hosted Python source files, to be included on the file
725725
system, via the [`files` setting](../user-guide/configuration/#files).
726726
4. Create a folder containing the package's files and sub folders, and create
727-
a hosted `.zip` or `.tgz`/`.tar.gz` archive to be decompressed into the file
727+
a hosted `.zip` or `.tgz`/`.tar.gz`/`.whl` archive to be decompressed into the file
728728
system (again, via the
729729
[`files` setting](../user-guide/configuration/#files)).
730+
5. provide your own `.whl` package as part of the `packages = [...]` list to see it available within your project
730731

731732
#### Host a package
732733

@@ -768,7 +769,7 @@ packages onto the Python path:
768769
</script>
769770
```
770771

771-
#### Code archive (`zip`/`tgz`)
772+
#### Code archive (`zip`/`tgz`/`whl`)
772773

773774
Compress all the code you want into an archive (using either either `zip` or
774775
`tgz`/`tar.gz`). Host the resulting archive and use the
@@ -1207,6 +1208,8 @@ js.callback(
12071208
)
12081209
```
12091210

1211+
Ultimately though, Pyodide maps can be consumed out of the box as literals, so that *some* required conversion might not be needed anymore and `to_js` might be enough, without specifying the `dict_converter` as that's inferred, once consumed, behind the scene.
1212+
12101213
In addition, MicroPython's version of `to_js` takes the opposite approach (for
12111214
many of the reasons stated above) and converts Python dictionaries to object
12121215
literals instead of `Map` objects.
@@ -1215,6 +1218,8 @@ As a result, **the PyScript `pyscript.ffi.to_js` ALWAYS returns a JavaScript
12151218
object literal by default when converting a Python dictionary** no matter if
12161219
you're using Pyodide or MicroPython as your interpreter.
12171220

1221+
That being said, in MicroPython things work closely to JS users' expectations, memory friendly too, so using the `to_js` is more a cross-interpreter guard than a necessity these days.
1222+
12181223
#### Caveat
12191224

12201225
!!! warning

docs/user-guide/builtins.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,27 @@ from pyscript import sync
571571
sync.hello("PyScript")
572572
```
573573
574+
### `pyscript.py_modules`
575+
576+
Bootstrapping *Pyodide* or *MicroPython* with a lot of packages might degrade the time to readyness.
577+
578+
When some dependency is needed only under certain conditions or after some specific user action, we are offering an asynchronuos way to lazily import packages that were not present already in the *config*.
579+
580+
A bare minimal example of how this feature works can be summarized as such:
581+
582+
```html title="pyscript.py_modules example"
583+
<script type="py" async>
584+
from pyscript import py_modules
585+
586+
matplotlib, regex, = await py_modules("matplotlib", "regex")
587+
588+
print(matplotlib, regex)
589+
</script>
590+
```
591+
592+
The `py_modules` then returns an asynchronous tuple with one or more modules passed as string and it's compatible with `.whl` packages too.
593+
594+
574595
## HTML attributes
575596

576597
As a convenience, and to ensure backwards compatibility, PyScript allows the

docs/user-guide/editor.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ or `mpy-editor` (for MicroPython), the plugin creates a visual code editor,
1616
with code highlighting and a "run" button to execute the editable code
1717
contained therein in a non-blocking worker.
1818

19+
!!! info
20+
21+
Once clicked, the *Run* button will show a spinner until the code is executed. This might not be visible if the code took nothing to execute, but if the code took any measurable time longer, one will notice such spinner before results will be shown.
22+
23+
1924
The interpreter is not loaded onto the page until the run button is clicked. By
2025
default each editor has its own independent instance of the specified
2126
interpreter:
@@ -61,6 +66,8 @@ The outcome of these code fragments should look something like this:
6166

6267
Hovering over the Python editor reveals the "run" button.
6368

69+
### Setup
70+
6471
Sometimes you need to create a pre-baked Pythonic context for a shared
6572
environment used by an editor. This need is especially helpful in educational
6673
situations where boilerplate code can be run, with just the important salient
@@ -128,6 +135,46 @@ not expect the same behavior regular *PyScript* elements follow, most notably:
128135
* There is no special reference to the underlying editor instance, while
129136
there is both `script.terminal` or `__terminal__` in the terminal.
130137

138+
## Read / Write / Execute
139+
140+
Behind the scene, we bootstrap an editor that provides:
141+
142+
* highlights around the Python code in it
143+
* a Run button to execute the code
144+
* a `target` reference where the code output lands, once printed
145+
146+
This is all great and sound, but there is also a way to read the *editor* code, and update it with ease, that's the `code` accessor any editor gets, once bootstrapped:
147+
148+
```python
149+
from pyscript import document
150+
151+
# grab the editor script reference
152+
editor = document.querySelector('#editor')
153+
154+
# output its content
155+
print(editor.code)
156+
157+
# or update its content
158+
editor.code = """
159+
a = 1
160+
b = 2
161+
print(a + b)
162+
"""
163+
```
164+
165+
To execute that new editor content a user might click the *Run* button one more time, or the driver of such editor can use `editor.process(editor.code)`, or any other arbitrary code, to actually bypass the need to click *Run* and execute the code passed along the `.process(...)` invoke.
166+
167+
These utilities are helpful to let consumers of the editor change its view state and/or execute it out the box.
168+
169+
## Config
170+
171+
Differently from `<script type="py">` or `<py-script>`, and the `mpy` counterpart, a *PyEditor* is not affected by the presence of `<py-config>` elements in the page: it requires an explicit `config="..."` attribute to specify its dependencies, behavior and whatnot.
172+
173+
If a `setup` editor is present though, that's the only *PyEditor* that needs a config, so that any further related editor will have already such config parsed and bootstrapped.
174+
175+
That is: do not expect `<py-config>` to dictate the behavior of a `py-editor`, these are *two different kind of custom script* so that an editor, when needed, must use a `config` attribute.
176+
177+
131178
## Still missing
132179

133180
The PyEditor is currently under active development and refinement, so features

docs/user-guide/terminal.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,23 @@ terminal automatically become links). Behind the scenes is the example code
150150
shown above, and this approach will work for
151151
[any other addon](https://github.com/xtermjs/xterm.js/tree/master/addons/) you
152152
may wish to use.
153+
154+
### MicroPython
155+
156+
When it comes to *MicroPython*, we recently enabled a proper *REPL* mode that offers an easier way to have a fully functional terminal, including:
157+
158+
* all *Ctrl+X* strokes are handled, including *paste mode* and kill switches
159+
* **history** works out of the box, *arrow up* and see what you pasted or typed before
160+
* **tab completion** works too, *tab* away to see your previously referenced variables or available globals
161+
* **copy and paste** improved, out of a singe terminal entry or a *paste mode* enabled variant
162+
163+
In few words, the *MicroPython* terminal is somehow superior and broadly aligned with a regular *MicroPython terminal / REPL* experience, and strawberry on top, it works on both *main* thread and *worker* related one, and these are the differences:
164+
165+
* **main thread terminal**
166+
* the `input` is, as blocking requirement, delegated to the native Web [prompt](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) utility
167+
* there is no guard against blocking executing code, such as `while True:` loops and friends
168+
* **worker thread terminal**
169+
* full support for `input` directives without ever blocking the main thread or showing *prompt* blocking relates UI around
170+
* `while True:` loops, or any other loop based, or blocking, directive, is handled out of the box without blocking the main thread UI
171+
172+
In short, we still encourage the usage of `worker` attribute to bootstrap a *MicroPython* terminal, but fear not, the script without such attribute will not throw anymore on *input* calls and it will just work almost as fine out of the main thread as long as nothing is really blocking such thread execution.

docs/user-guide/workers.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,82 @@ into the DOM and access some `window` based APIs.
146146
and produce unforeseen problematic results**. Remember, with great power
147147
comes great responsibility... and we've given you a bazooka (so please
148148
remember not to shoot yourself in the foot with it).
149+
150+
## PyWorker
151+
152+
It is possible to bootstrap either a *micropython* or a *pyodide* worker from either *micropython* or *pyodide* and within *Python* code.
153+
154+
Due different bootstrap time, the most common use case that is going to be tackled in here is *MicroPython* bootstrapping *Pyodide* out of *Python* code, underlying each step within the process.
155+
156+
#### Structure
157+
158+
For highlight goodness and simplicity sake, we are going to use an `mpy` script on the main page, which points at a `main.py` file that bootstraps a `worker.py` file.
159+
160+
**html**
161+
```HTML title="Pyodide worker via MicroPython"
162+
<!DOCTYPE html>
163+
<html lang="en">
164+
<head>
165+
<meta charset="utf-8">
166+
<meta name="viewport" content="width=device-width,initial-scale=1">
167+
<!-- PyScript CSS -->
168+
<link rel="stylesheet" href="https://pyscript.net/releases/2024.5.2/core.css">
169+
<!-- This script tag bootstraps PyScript -->
170+
<script type="module" src="https://pyscript.net/releases/2024.5.2/core.js"></script>
171+
<title>PyWorker - mpy bootstrapping pyodide example</title>
172+
<!-- the async attribute is useful but not mandatory -->
173+
<script type="mpy" src="main.py" async></script>
174+
</head>
175+
</html>
176+
```
177+
178+
**main.py**
179+
```Python title="MicroPython bootstrapping a Pyodide worker"
180+
from pyscript import PyWorker, document
181+
182+
# bootstrap the pyodide worker with optional config too
183+
# the worker here is:
184+
# * owned by this script, no JS or Pyodide code in the same page can access it
185+
# * it allows pre-sync methods exposure
186+
# * it exposes a ready Promise to await pyodide on the worker side
187+
# * it then allows using post-sync (utilities exposed by pyodide)
188+
worker = PyWorker("worker.py", type="pyodide")
189+
190+
# expose an utility that can be invoke *out of the box* in worker.py
191+
worker.sync.greetings = lambda: print("Pyodide bootstrapped")
192+
193+
print("before ready")
194+
# await for Pyodide to complete its bootstrap
195+
await worker.ready
196+
print("after ready")
197+
198+
# await any exposed utility exposed via Pyodide
199+
result = await worker.sync.heavy_computation()
200+
print(result)
201+
202+
# show the result at the end of the body
203+
document.body.append(result)
204+
205+
# here we free memory and get rid of everything
206+
worker.terminate()
207+
```
208+
209+
**worker.py**
210+
```Python title="A Pyodide worker"
211+
from pyscript import sync
212+
213+
# use any already exposed utility from main.py
214+
sync.greetings()
215+
216+
# expose any method meant to be used from main
217+
sync.heavy_computation = lambda: 6 * 7
218+
```
219+
220+
Save these files in a *tmp* folder and use `npx mini-coi ./tmp` to reach out that `index.html` and see the following outcome in *devtools*:
221+
222+
```
223+
before ready
224+
Pyodide bootstrapped
225+
after ready
226+
42
227+
```

0 commit comments

Comments
 (0)