Skip to content

Commit d51e294

Browse files
authored
Merge pull request #74 from pyscript/2024-03-02
Update docs to 2024.3.2 release.
2 parents af47c01 + 908b8b1 commit d51e294

File tree

8 files changed

+269
-15
lines changed

8 files changed

+269
-15
lines changed

Diff for: docs/beginning-pyscript.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ module in the document's `<head>` tag:
106106
<meta charset="utf-8" />
107107
<meta name="viewport" content="width=device-width,initial-scale=1" />
108108
<title>🦜 Polyglot - Piratical PyScript</title>
109-
<link rel="stylesheet" href="https://pyscript.net/releases/2024.3.1/core.css">
110-
<script type="module" src="https://pyscript.net/releases/2024.3.1/core.js"></script>
109+
<link rel="stylesheet" href="https://pyscript.net/releases/2024.3.2/core.css">
110+
<script type="module" src="https://pyscript.net/releases/2024.3.2/core.js"></script>
111111
</head>
112112
<body>
113113

@@ -157,8 +157,8 @@ In the end, our HTML should look like this:
157157
<meta charset="utf-8" />
158158
<meta name="viewport" content="width=device-width,initial-scale=1" />
159159
<title>🦜 Polyglot - Piratical PyScript</title>
160-
<link rel="stylesheet" href="https://pyscript.net/releases/2024.3.1/core.css">
161-
<script type="module" src="https://pyscript.net/releases/2024.3.1/core.js"></script>
160+
<link rel="stylesheet" href="https://pyscript.net/releases/2024.3.2/core.css">
161+
<script type="module" src="https://pyscript.net/releases/2024.3.2/core.js"></script>
162162
</head>
163163
<body>
164164
<h1>Polyglot 🦜 💬 🇬🇧 ➡️ 🏴‍☠️</h1>

Diff for: docs/user-guide/builtins.md

+15
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,21 @@ response = await fetch("https://example.com", method="POST", body="HELLO").text(
193193
bug). However, you could use a pass-through proxy service to get around
194194
this limitation (i.e. the proxy service makes the call on your behalf).
195195

196+
### `pyscript.ffi.to_js`
197+
198+
A utility function to convert Python references into their JavaScript
199+
equivalents. For example, a Python dictionary is converted into a JavaScript
200+
object literal (rather than a JavaScript `Map`), unless a `dict_converter`
201+
is explicitly specified and the runtime is Pyodide.
202+
203+
### `pyscript.ffi.create_proxy`
204+
205+
A utility function explicitly for when a callback function is added via an
206+
event listener. It ensures the function still exists beyond the assignment of
207+
the function to an event. Should you not `create_proxy` around the callback
208+
function, it will be immediately garbage collected after being bound to the
209+
event.
210+
196211
## Main-thread only features
197212

198213
### `pyscript.PyWorker`

Diff for: docs/user-guide/configuration.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ examples could be equivalently re-written as:
160160
... etc ...
161161
```
162162

163+
If the source part of the configuration is either a `.zip` or `.tar.gz` file
164+
and its destination is a folder path followed by a star (e.g. `/*` or
165+
`./dest/*`), then PyScript will extract the referenced archive automatically
166+
into the target directory in the browser's built in file system.
163167

164168
!!! warning
165169

@@ -312,12 +316,9 @@ plugins = ["custom_plugin", "!error"]
312316

313317
### JavaScript modules
314318

315-
It's easy to import and use JavaScript modules in your Python code.
316-
317-
!!! warning
318-
319-
This feature currently only works with Pyodide. MicroPython support will
320-
come in a future release.
319+
It's easy to import and use JavaScript modules in your Python code. This
320+
section of the docs examines the configuration needed to make this work. How
321+
to make use of JavaScript is dealt with [elsewhere](../dom/#working-with-javascript).
321322

322323
To do so, requires telling PyScript about the JavaScript modules you want to
323324
use. This is the purpose of the `js_modules` related configuration fields.

Diff for: docs/user-guide/dom.md

+238
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ equivalent values: `["hello", 1, 2, 3]`.
6868
instantiation very differently. By explicitly calling the JavaScript
6969
class's `new` method PyScript both signals and honours this difference.
7070

71+
Should you require lower level API access to FFI features, you can find such
72+
builtin functions under the `pyscript.ffi` namespace in both Pyodide and
73+
MicroPython. The available functions are described in our section on
74+
[builtin helpers](../builtins).
7175

7276
## PyDom
7377

@@ -253,3 +257,237 @@ paragraphs.style['background-color'] = 'lightyellow'
253257
a collection by setting the proper CSS rules, using `style` with the same API as a dictionary.
254258
* `html`: allows to change the `html` attribute on all the elements of a collection.
255259
* `value`: allows to change the `value` attribute on all the elements of a collection.
260+
261+
## Working with JavaScript
262+
263+
There are three ways in which JavaScript can get into a web page.
264+
265+
1. As a global reference attached to the `window` object in the web page
266+
because the code was referenced as the source of a `script` tag in your HTML
267+
(the very old school way to do this).
268+
2. Using the [Universal Module Definition](https://github.com/umdjs/umd) (UMD)
269+
- an out-of-date and non-standard way to create JavaScript modules.
270+
3. As a standard
271+
[JavaScript Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
272+
which is the modern, standards compliant way to define and use a JavaScript
273+
module. If possible, this is the way you should do things.
274+
275+
Sadly, this being the messy world of the web, methods 1 and 2 are still quite
276+
common, and so you need to know about them so you're able to discern and work
277+
around them. There's nothing WE can do about this situation, but we can
278+
suggest "best practice" ways to work around each situation.
279+
280+
Remember, as mentioned
281+
[elsewhere in our documentation](../configuration/#javascript-modules),
282+
the standard way to get JavaScript modules into your PyScript Python context is
283+
to link a _source_ standard JavaScript module to a _destination_ name:
284+
285+
```toml title="Reference a JavaScript module in the configuration."
286+
[js_modules.main]
287+
"https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet-src.esm.js" = "leaflet"
288+
```
289+
290+
Then, reference the module via the destination name in your Python code, by
291+
importing it from the `pyscript.js_modules` namespace:
292+
293+
```python title="Import the JavaScript module into Python"
294+
from pyscript.js_modules import leaflet as L
295+
296+
map = L.map("map")
297+
298+
# etc....
299+
```
300+
301+
We'll deal with each of the potential JavaScript related situations in turn:
302+
303+
### JavaScript as a global reference
304+
305+
In this situation, you have some JavaScript code that just globally defines
306+
"stuff" in the context of your web page via a `script` tag. Your HTML will
307+
contain something like this:
308+
309+
```html title="JavaScript as a global reference"
310+
<!doctype html>
311+
<!--
312+
This JS utility escapes and unescapes HTML chars. It adds an "html" object to
313+
the global context.
314+
-->
315+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.js"></script>
316+
317+
<!--
318+
Vanilla JS just to check the expected object is in the global context of the
319+
web page.
320+
-->
321+
<script>
322+
console.log(html);
323+
</script>
324+
```
325+
326+
When you find yourself in this situation, simply use the `window` object in
327+
your Python code (found in the `pyscript` namespace) to interact with the
328+
resulting JavaScript objects:
329+
330+
```python title="Python interaction with the JavaScript global reference"
331+
from pyscript import window, document
332+
333+
334+
# The window object is the global context of your web page.
335+
html = window.html
336+
337+
# Just use the object "as usual"...
338+
# e.g. show escaped HTML in the body: &lt;&gt;
339+
document.body.append(html.escape("<>"))
340+
```
341+
342+
You can find an example of this technique here:
343+
344+
[https://pyscript.com/@agiammarchi/floral-glade/v1](https://pyscript.com/@agiammarchi/floral-glade/v1)
345+
346+
### JavaScript as a non-standard UMD module
347+
348+
Sadly, these sorts of non-standard JavaScript modules are still quite
349+
prevalent. But the good news is there are strategies you can use to help you
350+
get them to work properly.
351+
352+
The non-standard UMD approach tries to check for `export` and `module` fields
353+
in the JavaScript module and, if it doesn’t find them, falls back to treating
354+
the module in the same way as a global reference described above.
355+
356+
If you find you have a UMD JavaScript module, there are services online to
357+
automagically convert it to the modern and standards compliant way to d
358+
o JavaScript modules. A common (and usually reliable) service is provided by
359+
[https://esm.run/your-module-name](https://esm.run/your-module-name), a
360+
service that provides an out of the box way to consume the module in the
361+
correct and standard manner:
362+
363+
```html title="Use esm.run to automatically convert a non-standard UMD module"
364+
<!doctype html>
365+
<script type="module">
366+
// this utility escapes and unescape HTML chars
367+
import { escape, unescape } from "https://esm.run/html-escaper";
368+
// esm.run returns a module ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
369+
console.log(escape("<>"));
370+
// log: "&lt;&gt;"
371+
</script>
372+
```
373+
374+
If a similar test works for the module you want to use, use the esm.run CDN
375+
service within the `py` or `mpy` configuration file as explained at the start
376+
of this section on JavaScript (i.e. you'll use it via the `pyscript.js_modules`
377+
namespace).
378+
379+
If this doesn't work, assume the module is not updated nor migrated to a state
380+
that can be automatically translated by services like esm.run. You could try an
381+
alternative (more modern) JavaScript module to achieve you ends or (if it
382+
really must be this module), you can wrap it in a new JavaScript module that
383+
conforms to the modern standards.
384+
385+
The following four files demonstrate this approach:
386+
387+
```html title="index.html - still grab the script so it appears as a global reference."
388+
<!doctype html>
389+
...
390+
<!-- land the utility still globally as generic script -->
391+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.js"></script>
392+
...
393+
```
394+
395+
```js title="wrapper.js - this grabs the JavaScript functionality from the global context and wraps it (exports it) in the modern standards compliant manner."
396+
// get all utilities needed from the global.
397+
const { escape, unescape } = globalThis.html;
398+
399+
// export utilities like a standards compliant module would do.
400+
export { escape, unescape };
401+
```
402+
403+
```toml title="pyscript.toml - configure your JS modules as before, but use your wrapper instead of the original module."
404+
[js_modules.main]
405+
# will simulate a standard JS module
406+
"./wrapper.js" = "html_escaper"
407+
```
408+
409+
```python title="main.py - just import the module as usual and make use of it."
410+
from pyscript import document
411+
412+
# import the module either via
413+
from pyscript.js_modules import html_escaper
414+
# or via
415+
from pyscript.js_modules.html_escaper import escape, unescape
416+
417+
# show on body: &lt;&gt;
418+
document.body.append(html.escape("<>"))
419+
```
420+
421+
You can see this approach in action here:
422+
423+
[https://pyscript.com/@agiammarchi/floral-glade/v2](https://pyscript.com/@agiammarchi/floral-glade/v2)
424+
425+
### A standard JavaScript module
426+
427+
This is both the easiest and best way to import any standard JS module into
428+
Python.
429+
430+
You don't need to reference the script in your HTML, just define how the source
431+
JavaScript module maps into the `pyscript.js_modules` namespace in your
432+
configuration file, as explained above.
433+
434+
That's it!
435+
436+
Here is an example project that uses this approach:
437+
438+
[https://pyscript.com/@agiammarchi/floral-glade/v3](https://pyscript.com/@agiammarchi/floral-glade/v3)
439+
440+
441+
### My own JavaScript code
442+
443+
If you have your own JavaScript work, just remember to write it as a standard
444+
JavaScript module. Put simply, ensure you `export` the things you need to. For
445+
instance, in the following fragment of JavaScript, the two functions are
446+
exported from the module:
447+
448+
```js title="code.js - containing two functions exported as capabilities of the module."
449+
/*
450+
Some simple JavaScript functions for example purposes.
451+
*/
452+
453+
export function hello(name) {
454+
return "Hello " + name;
455+
}
456+
457+
export function fibonacci(n) {
458+
if (n == 1) return 0;
459+
if (n == 2) return 1;
460+
return fibonacci(n - 1) + fibonacci(n - 2);
461+
}
462+
```
463+
464+
Next, just reference this module in the usual way in your TOML or JSON
465+
configuration file:
466+
467+
```TOML title="pyscript.toml - references the code.js module so it will appear as the code module in the pyscript.js_modules namespace."
468+
[js_modules.main]
469+
"code.js" = "code"
470+
```
471+
472+
In your HTML, reference your Python script with this configuration file:
473+
474+
```html title="Reference the expected configuration file."
475+
<script type="py" src="./main.py" config="./pyscript.toml" terminal></script>
476+
```
477+
478+
Finally, just use your JavaScript module’s exported functions inside PyScript:
479+
480+
```python title="Just call your bespoke JavaScript code from Python."
481+
from pyscript.js_modules import code
482+
483+
484+
# Just use the JS code from Python "as usual".
485+
greeting = code.hello("Chris")
486+
print(greeting)
487+
result = code.fibonacci(12)
488+
print(result)
489+
```
490+
491+
You can see this in action in the following example project:
492+
493+
[https://pyscript.com/@ntoll/howto-javascript/latest](https://pyscript.com/@ntoll/howto-javascript/latest)

Diff for: docs/user-guide/first-steps.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ CSS:
2020
<meta charset="UTF-8">
2121
<meta name="viewport" content="width=device-width,initial-scale=1.0">
2222
<!-- PyScript CSS -->
23-
<link rel="stylesheet" href="https://pyscript.net/releases/2024.3.1/core.css">
23+
<link rel="stylesheet" href="https://pyscript.net/releases/2024.3.2/core.css">
2424
<!-- This script tag bootstraps PyScript -->
25-
<script type="module" src="https://pyscript.net/releases/2024.3.1/core.js"></script>
25+
<script type="module" src="https://pyscript.net/releases/2024.3.2/core.js"></script>
2626
</head>
2727
<body>
2828
<!-- your code goes here... -->

Diff for: docs/user-guide/plugins.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Here's an example of how a PyScript plugin looks like:
1414

1515
```js
1616
// import the hooks from PyScript first...
17-
import { hooks } from "https://pyscript.net/releases/2024.3.1/core.js";
17+
import { hooks } from "https://pyscript.net/releases/2024.3.2/core.js";
1818

1919
// Use the `main` attribute on hooks do define plugins that run on the main thread
2020
hooks.main.onReady.add((wrap, element) => {

Diff for: mkdocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ nav:
7171
- First steps: user-guide/first-steps.md
7272
- Architecture: user-guide/architecture.md
7373
- Configuration: user-guide/configuration.md
74-
- The DOM: user-guide/dom.md
74+
- The DOM &amp; JavaScript: user-guide/dom.md
7575
- Workers: user-guide/workers.md
7676
- Builtin helpers: user-guide/builtins.md
7777
- Python terminal: user-guide/terminal.md

Diff for: version.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "2024.3.1"
2+
"version": "2024.3.2"
33
}

0 commit comments

Comments
 (0)