Skip to content

Commit 64cf8ef

Browse files
committed
Add terminal section, minor edits, and split user guide into separate pages.
1 parent c07ddf4 commit 64cf8ef

17 files changed

+1236
-1147
lines changed

docs/assets/images/py-terminal.gif

136 KB
Loading

docs/assets/images/pyterm1.png

8.34 KB
Loading

docs/assets/images/pyterm2.gif

64.9 KB
Loading

docs/user-guide.md

Lines changed: 0 additions & 1147 deletions
This file was deleted.

docs/user-guide/architecture.md

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# Architecture, Lifecycle & Interpreters
2+
3+
## Core concepts
4+
5+
PyScript's architecture has two core concepts:
6+
7+
1. A small, efficient and powerful kernel called
8+
[PolyScript](https://github.com/pyscript/polyscript) is the foundation
9+
upon which PyScript and plugins are built.
10+
2. The PyScript [stack](https://en.wikipedia.org/wiki/Solution_stack) inside
11+
the browser is simple and clearly defined.
12+
13+
### PolyScript
14+
15+
[PolyScript](https://github.com/pyscript/polyscript) is the core of PyScript.
16+
17+
!!! danger
18+
19+
Unless you are an advanced user, you only need to know that PolyScript
20+
exists, and it can be safely ignored.
21+
22+
PolyScript's purpose is to bootstrap the platform and provide all the necessary
23+
core capabilities. Setting aside PyScript for a moment, to use
24+
*just PolyScript* requires a `<script>` reference to it, along with a further
25+
`<script>` tag defining how to run your code.
26+
27+
```html title="Bootstrapping with PolyScript"
28+
<!doctype html>
29+
<html>
30+
<head>
31+
<!-- this is a way to automatically bootstrap polyscript -->
32+
<script type="module" src="https://cdn.jsdelivr.net/npm/polyscript"></script>
33+
</head>
34+
<body>
35+
<!--
36+
Run some Python code with the MicroPython interpreter, but without
37+
the extra benefits provided by PyScript.
38+
-->
39+
<script type="micropython">
40+
from js import document
41+
document.body.textContent = 'polyscript'
42+
</script>
43+
</body>
44+
</html>
45+
```
46+
47+
!!! warning
48+
49+
**PolyScript is not PyScript.**
50+
51+
PyScript enhances the available Python interpreters with convenient
52+
features, helper functions and easy-to-use yet powerful capabilities.
53+
54+
These enhancements are missing from PolyScript.
55+
56+
PolyScript's capabilities, upon which PyScript is built, can be summarised as:
57+
58+
* Evaluation of code via [`<script>` tags](https://pyscript.github.io/polyscript/#how-scripts-work).
59+
* Handling
60+
[browser events](https://pyscript.github.io/polyscript/#how-events-work)
61+
via code evaluated with an interpreter supported by PolyScript.
62+
* A [clear way to use workers](https://pyscript.github.io/polyscript/#xworker)
63+
via the `XWorker` class and its related reference, `xworker`.
64+
* [Custom scripts](https://pyscript.github.io/polyscript/#custom-scripts) to
65+
enrich PolyScript's capabilities.
66+
* A [ready event](https://pyscript.github.io/polyscript/#ready-event)
67+
dispatched when an interpreter is ready and about to run code, and a
68+
[done event](https://pyscript.github.io/polyscript/#done-event) when an
69+
interpreter has finished evaluating code.
70+
* [Hooks](https://pyscript.github.io/polyscript/#hooks), called at clearly
71+
defined moments in the page lifecycle, provide a means of calling user
72+
defined functions to modify and enhance PolyScript's default behaviour.
73+
* [Multipe interpreters](https://pyscript.github.io/polyscript/#interpreter-features)
74+
(in addition to Pyodide and MicroPython, PolyScript works with Lua and Ruby -
75+
although these are beyond the scope of this project).
76+
77+
### The stack
78+
79+
The stack describes how the different building blocks _inside_ a PyScript
80+
application relate to each other:
81+
82+
<img src="../../assets/images/platform.png"/>
83+
84+
* Everything happens inside the context of the browser (represented by the
85+
black border). **The browser tab for your PyScript app is your
86+
sandboxed computing environment**.
87+
88+
!!! failure
89+
90+
PyScript is simply Python running in the browser. It is an unfamiliar
91+
concept that some fail to remember.
92+
93+
* PyScript isn't running on a server hosted in the cloud.
94+
* PyScript doesn't use the version of Python running natively on the user's
95+
operating system.
96+
* **PyScript only runs IN THE BROWSER (and nowhere else).**
97+
98+
* At the bottom of the stack are the Python interpreters compiled to WASM. They
99+
evaluate your code and interact with the browser via the [FFI](dom.md/#ffi).
100+
* The PyScript layer makes it easy to use and configure the Python
101+
interpreters. There are two parts to this:
102+
1. The PolyScript kernel (see above), that bootstraps everything and
103+
provides the core capabilities.
104+
2. PyScript and related plugins that sit atop PolyScript to give us an
105+
easy-to-use Python platform _in the browser_.
106+
* Above the PyScript layer are either:
107+
1. Application frameworks, modules and libraries written in Python that you
108+
use to create useful applications.
109+
2. Your code (that's your responsibility).
110+
111+
## Lifecycle
112+
113+
If the architecture explains how components relate to each other, the lifecycle
114+
explains how things unfold. It's important to understand both: it will
115+
help you think about your own code and how it sits within PyScript.
116+
117+
Here's how PyScript unfolds through time:
118+
119+
* The browser is directed to a URL. The response is HTML.
120+
* When parsing the HTML response the browser encounters the `<script>`
121+
tag that references PyScript. PyScript is loaded and evaluated as a
122+
[JavaScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules),
123+
meaning it doesn't hold up the loading of the page and is only evaluated when
124+
the HTML is fully parsed.
125+
* The PyScript module does broadly six things:
126+
1. Discover Python code referenced in the page.
127+
2. Evaluate any [configuration](configuration.md) on the page (either via
128+
single `<py-config>` or `<mpy-config>` tags **or** the `config`
129+
attribute of a `<script>`, `<py-script>` or `<mpy-script>` tag).
130+
3. Given the detected configuration, download the required interpreter.
131+
4. Setup the interpreter's environment. This includes any
132+
[plugins](configuration.md/#plugins), [packages](configuration.md/#packages) or [files](configuration.md/#files) that need
133+
to be loaded.
134+
5. Make available various
135+
[builtin helper objects and functions](builtins.md) to the
136+
interpreter's environment (accessed via the `pyscript` module).
137+
6. Only then use the interpreter in the correctly configured environment to
138+
evaluate the detected Python code.
139+
* When an interpreter is ready the `py:ready` or `mpy:ready` events are
140+
dispatched, depending which interpreter you've specified (Pyodide or
141+
MicroPython respectively).
142+
* Finally, a `py:done` event is dispatched after every single script
143+
referenced from the page has finished.
144+
145+
In addition, various "hooks" are called at different moments in the lifecycle
146+
of PyScript. These can be used by plugin authors to modify or enhance the
147+
behaviour of PyScript. The hooks, and how to use them, are explored further in
148+
[the section on plugins](plugins.md).
149+
150+
!!! warning
151+
152+
A web page's workers have completely separate environments to the main
153+
thread.
154+
155+
It means configuration in the main thread can be different to that for
156+
an interpreter running on a worker. In fact, you can use different
157+
interpreters and configuration in each context (for instance, MicroPython
158+
on the main thread, and Pyodide on a worker).
159+
160+
Think of workers as completely separate sub-processes inside your browser
161+
tab.
162+
163+
## Interpreters
164+
165+
Python is an interpreted language, and thus needs an interpreter to work.
166+
167+
PyScript supports two versions of the Python interpreter that have
168+
been compiled to WASM: Pyodide and MicroPython. You should select which one to
169+
use depending on your use case and acceptable trade-offs.
170+
171+
!!! info
172+
173+
In future, and subject to progress, we hope to make available a third
174+
Pythonic option: [SPy](https://github.com/spylang/spy), a staticially typed
175+
version of Python compiled directly to WASM.
176+
177+
Both interpreters make use of [emscripten](https://emscripten.org/), a compiler
178+
toolchain (using [LLVM](https://llvm.org/)), for emitting WASM assets for the
179+
browser. Emscripten also provides APIs so operating-system level features such
180+
as a sandboxed [file system](https://emscripten.org/docs/api_reference/Filesystem-API.html)
181+
(**not** the user's local machine's filesystem), [IO](https://emscripten.org/docs/api_reference/console.h.html)
182+
(`stdin`, `stdout`, `stderr` etc,) and [networking](https://emscripten.org/docs/api_reference/fetch.html) are
183+
available within the context of a browser.
184+
185+
Both Pyodide and MicroPython implement the same robust
186+
[Python](https://pyodide.org/en/stable/usage/api/python-api.html)
187+
[JavaScript](https://pyodide.org/en/stable/usage/api/js-api.html)
188+
[foreign function interface](dom.md/#ffi) (FFI). This
189+
bridges the gap between the browser and Python worlds.
190+
191+
### Pyodide
192+
193+
<a href="https://pyodide.org/"><img src="../../assets/images/pyodide.png"/></a>
194+
195+
[Pyodide](https://pyodide.org/) is a version of the standard
196+
[CPython](https://python.org/) interpreter, patched to compile to WASM and
197+
work in the browser.
198+
199+
It includes many useful features:
200+
201+
* The installation of pure Python packages from [PyPI](https://pypi.org/) via
202+
the [micropip](https://micropip.pyodide.org/en/stable/index.html) package
203+
installer.
204+
* An active, friendly and technically outstanding team of volunteer
205+
contributors (some of whom have been supported by the PyScript project).
206+
* Extensive official
207+
[documentation](https://micropip.pyodide.org/en/stable/index.html), and many
208+
tutorials found online.
209+
* Builds of Pyodide that include popular packages for data science like
210+
[Numpy](https://numpy.org/), [Scipy](https://scipy.org/) and
211+
[Pandas](https://pandas.pydata.org/).
212+
213+
!!! warning
214+
215+
You may encounter an error message from `micropip` that explains it can't
216+
find a "pure Python wheel" for a package. Pyodide's documentation
217+
[explains what to do in this situation](https://pyodide.org/en/stable/usage/faq.html#why-can-t-micropip-find-a-pure-python-wheel-for-a-package).
218+
219+
Briefly, some
220+
[packages with C extensions](https://docs.python.org/3/extending/building.html)
221+
have versions compiled for WASM and these can be installed with `micropip`.
222+
Packages containing C extensions that _are not compiled for WASM_ cause the
223+
"pure Python wheel" error.
224+
225+
There are plans afoot to make WASM a target in PyPI so packages with C
226+
extenions are automatically compiled to WASM.
227+
228+
### MicroPython
229+
230+
<a href="https://micropython.org/"><img src="../../assets/images/micropython.png"/></a>
231+
232+
[MicroPython](https://micropython.org/) is a lean and efficient implementation
233+
of the Python 3 programming language that includes a small subset of the Python
234+
standard library and is optimised to run on microcontrollers and in constrained
235+
environments (like the browser).
236+
237+
Everything needed to view a web page in a browser needs to be delivered
238+
over the network. The smaller the asset to be delivered can be, the better.
239+
MicroPython, when compressed for delivery to the browser, is only around
240+
170k in size - smaller than many images found on the web.
241+
242+
This makes MicroPython particularly suited to browsers running in a more
243+
constrained environment such as on a mobile or tablet based device. Browsing
244+
with these devices often uses (slower) mobile internet connections.
245+
Furthermore, because MicroPython is lean and efficient it still performs
246+
exceptionally well on these relatively underpowered devices.
247+
248+
Thanks to collaboration between the MicroPython and PyScript projects, there is
249+
a foreign function interface for MicroPython. The MicroPython FFI deliberately
250+
copies the API of the FFI originally written for Pyodide - meaning it is
251+
relatively easy to migrate between the two supported interpreters.

docs/user-guide/builtins.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Builtin helpers
2+
3+
PyScript makes available convenience objects and functions inside
4+
Python. This is done via the `pyscript` module:
5+
6+
```python title="Accessing the document object via the pyscript module"
7+
from pyscript import document
8+
```
9+
10+
## Common features
11+
12+
These objects / functions are available in both the main thread and in code
13+
running on a web worker:
14+
15+
### `pyscript.window`
16+
17+
This object is a proxy for the web page's
18+
[global window context](https://developer.mozilla.org/en-US/docs/Web/API/Window).
19+
20+
!!! warning
21+
22+
Please note that in workers, this is still the main window, not the
23+
worker's own global context. A worker's global context is reachable instead
24+
via `import js` (the `js` object being a proxy for the worker's
25+
`globalThis`).
26+
27+
### `pyscript.document`
28+
29+
This object is a proxy for the the web page's
30+
[document object](https://developer.mozilla.org/en-US/docs/Web/API/Document).
31+
The `document` is a representation of the
32+
[DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_object_model/Using_the_Document_Object_Model)
33+
and can be used to manipulate the content of the web page.
34+
35+
### `pyscript.display`
36+
37+
A function used to display content. The function is intelligent enough to
38+
introspect the object[s] it is passed and work out how to correctly display the
39+
object[s] in the web page.
40+
41+
The `display` function takes a list of `*values` as its first argument, and has
42+
two optional named arguments:
43+
44+
* `target=None` - the DOM element into which the content should be placed.
45+
* `append=True` - a flag to indicate if the output is going to be appended to
46+
the `target`.
47+
48+
There are some caveats:
49+
50+
* When used in the main thread, the `display` function automatically uses
51+
the current `<script>` tag as the `target` into which the content will
52+
be displayed.
53+
* If the `<script>` tag has the `target` attribute, the element on the page
54+
with that ID (or which matches that selector) will be used to display
55+
the content instead.
56+
* When used in a worker, the `display` function needs an explicit
57+
`target="dom-id"` argument to identify where the content will be
58+
displayed.
59+
* In both the main thread and worker, `append=True` is the default
60+
behaviour.
61+
62+
### `pyscript.when`
63+
64+
A Python decorator to indicate the decorated function should handle the
65+
specified events for selected elements.
66+
67+
The decorator takes two parameters:
68+
69+
* The `event_type` should be the name of the
70+
[browser event to handle](https://developer.mozilla.org/en-US/docs/Web/Events)
71+
as a string (e.g. `"click"`).
72+
* The `selector` should be a string containing a
73+
[valid selector](https://developer.mozilla.org/en-US/docs/Web/API/Document_object_model/Locating_DOM_elements_using_selectors)
74+
to indicate the target elements in the DOM whose events of `event_type` are
75+
of interest.
76+
77+
The following example has a button with an id of `my_button` and a decorated
78+
function that handles `click` events dispatched by the button.
79+
80+
```html title="The HTML button"
81+
<button id="my_button">Click me!</button>
82+
```
83+
84+
```python title="The decorated Python function to handle click events"
85+
from pyscript import when, display
86+
87+
88+
@when("click", "#my_button")
89+
def click_handler(event):
90+
"""
91+
Event handlers get an event object representing the activity that raised
92+
them.
93+
"""
94+
display("I've been clicked!")
95+
```
96+
97+
## Main-thread only features
98+
99+
### `pyscript.PyWorker`
100+
101+
A class used to instantiate a new worker from within Python.
102+
103+
!!! danger
104+
105+
Currently this only works with Pyodide.
106+
107+
The following fragment demonstrates who to start the Python code in the file
108+
`worker.py` on a new worker from within Python.
109+
110+
```python title="Starting a new worker from Python"
111+
from pyscript import PyWorker
112+
113+
114+
a_worker = PyWorker("./worker.py")
115+
```
116+
117+
## Worker only features
118+
119+
### `pyscript.sync`
120+
121+
A function used to pass serializable data from workers to the main thread.
122+
123+
Imagine you have this code on the main thread:
124+
125+
```python title="Python code on the main thread"
126+
from pyscript import PyWorker
127+
128+
def hello(name="world"):
129+
display(f"Hello, {name}")
130+
131+
worker = PyWorker("./worker.py")
132+
worker.sync.hello = hello
133+
```
134+
135+
In the code on the worker, you can pass data back to handler functions like
136+
this:
137+
138+
```python title="Pass data back to the main thread from a worker"
139+
from pyscript import sync
140+
141+
sync.hello("PyScript")
142+
```

0 commit comments

Comments
 (0)