|
| 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. |
0 commit comments