Skip to content

Commit

Permalink
Merge pull request #135 from pyscript/2024-8-1
Browse files Browse the repository at this point in the history
2024 8 1
  • Loading branch information
ntoll authored Aug 1, 2024
2 parents 126d9d6 + a25b637 commit ea0c9a0
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 131 deletions.
19 changes: 12 additions & 7 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,29 +413,34 @@ store = await storage("my-data-store", storage_class=MyStorage)

### `pyscript.web`

TODO: Use `display(element)` not `element.display()`.

The classes and references in this namespace provide a Pythonic way to interact
with the DOM. An explanation for how to idiomatically use this API can be found
[in the user guide](../user-guide/dom/#pyscriptweb)

#### `pyscript.web.dom`
#### `pyscript.web.page`

This object has two attributes and a single method:
This object represents a web page. It has four attributes and two methods:

* `html` - a reference to a Python object representing the [document's html](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html) root element.
* `head` - a reference to a Python object representing the [document's head](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head).
* `body` - a reference to a Python object representing the [document's body](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body).
* `title` - the page's [title](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title) (usually displayed in the browser's title bar or a page's tab.
* `find` - a method that takes a single [selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors)
argument and returns a collection of Python objects representing the matching
elements.
* `append` - a shortcut for `page.body.append` (to add new elements to the
page).

You may also shortcut the `find` method by enclosing a CSS selector in square
brackets: `page["#my-thing"]`.

These are provided as a convenience so you have several simple and obvious
options for accessing the content of the page (DOM).
options for accessing and changing the content of the page.

All the Python objects returned by these attributes and method are instances of
classes defined in the `pyscript.web.elements` namespace.
classes relating to HTML elements defined in the `pyscript.web` namespace.

#### `pyscript.web.elements.*`
#### `pyscript.web.*`

There are many classes in this namespace. Each is a one-to-one mapping of any
HTML element name to a Python class representing the HTML element of that
Expand Down
8 changes: 4 additions & 4 deletions docs/beginning-pyscript.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ module in the document's `<head>` tag:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>🦜 Polyglot - Piratical PyScript</title>
<link rel="stylesheet" href="https://pyscript.net/releases/2024.7.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.7.1/core.js"></script>
<link rel="stylesheet" href="https://pyscript.net/releases/2024.8.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.8.1/core.js"></script>
</head>
<body>

Expand Down Expand Up @@ -168,8 +168,8 @@ In the end, our HTML should look like this:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>🦜 Polyglot - Piratical PyScript</title>
<link rel="stylesheet" href="https://pyscript.net/releases/2024.7.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.7.1/core.js"></script>
<link rel="stylesheet" href="https://pyscript.net/releases/2024.8.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.8.1/core.js"></script>
</head>
<body>
<h1>Polyglot 🦜 💬 🇬🇧 ➡️ 🏴‍☠️</h1>
Expand Down
75 changes: 28 additions & 47 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ encounter.

### SharedArrayBuffer

This is the first and most common error users may encounter with PyScript.
This is the first and most common error users may encounter with PyScript:

!!! failure

Expand All @@ -58,52 +58,35 @@ This is the first and most common error users may encounter with PyScript.
you see this message:

```
Unable to use SharedArrayBuffer due insecure environment.
Please read requirements in MDN: ...
Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer
```

The error contains
[a link to MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
but it's the amount of content provided on this topic is overwhelming.

#### When

This error happens when **the server delivering your PyScript application is
incorrectly configured**. It fails to provide the correct headers to handle
security concerns for web workers, or you're not using
[mini-coi](https://github.com/WebReflection/mini-coi#readme) as an alternative
solution. (These requirements are explored
[in the worker page](../user-guide/workers#http-headers).)

**And** at least one of the following scenarios is true:

* There is a `worker` attribute in the *py* or *mpy* script element and the
[sync_main_only](https://pyscript.github.io/polyscript/#extra-config-features)
flag is not present or not `true`.
* There is a `<script type="py-editor">` that uses a *worker* behind the
scenes.
* There is an explicit `PyWorker` or `MPWorker` bootstrapping somewhere in your
code.

!!! info
This happens when you're unable to access objects in the main thread (`window`
and `document`) from code running in a web worker.

If `sync_main_only` is `true` then interactions between the main thread and
workers are limited to one way calls from the main thread to methods
exposed by workers.
This error happens because **the server delivering your PyScript application is
incorrectly configured** or **a `service-worker` attribute has not been used in
your `script` element**.

If `sync_main_only = true`, the following caveats apply:
Specifically, one of the following three problem situations applies to your
code:

* It is not possible to manipulate the DOM or do anything meaningful on the
main thread **from a worker**. This is because Atomics cannot guarantee
sync-like locks between a worker and the main thread.
* Only a worker's `pyscript.sync` methods are exposed, and they can only be
awaited from the main thread.
* The worker can only `await` main thread references one after the other, so
developer experience is degraded when one needs to interact with the
main thread.
* Because of the way your web server is configured, the browser limits the use
of a technology called "Atomics" (you don't need to know how it works, just
that it may be limited by the browser). If there is a `worker` attribute in
your `script` element, and your Python code uses the `window` or `document`
objects (that actually exist on the main thread), then the browser limitation
on Atomics will cause the failure, unless you reconfigure your server.
* There is a `<script type="py-editor">` (that must always use a worker behind
the scenes) and no fallback has been provided via a `service-worker`
attribute on that element.
* There is an explicit `PyWorker` or `MPWorker` instance **bootstrapping
somewhere in your code** and no `service_worker` fallback has been provided.

If your project simply bootstraps on the main thread, none of this is relevant
because no worker requires such special features.
All these cases have been documented with code examples and possible solutions
in our section on [web workers](../user-guide/workers/).

#### Why

Expand All @@ -122,21 +105,19 @@ CPU. It idles until the referenced index of the shared buffer changes,
effectively never blocking the main thread while still pausing its own
execution until the buffer's index is changed.

As overwhelming or complicated as this might sounds, these two fundamental
As overwhelming or complicated as this might sound, these two fundamental
primitives make main ↔ worker interoperability an absolute wonder in term of
developer experience. Therefore, we encourage folks to prefer using workers
over running Python in the main thread. This is especially so when using
Pyodide related projects, because of its heavier bootstrap or computation
requirements. Using workers ensures the main thread (and thus, the user
interface) remains unblocked.

Unfortunately, due to security concerns and potential attacks to shared
buffers, each server or page needs to allow extra security to prevent malicious
software to read or write into these buffers. But be assured that if you own
your code, your project, and you trust the modules or 3rd party code you need
and use, **there are less likely to be security concerns around this topic
within your project**. This situation is simply an unfortunate "*one rule catch
all*" standard any server can either enable or disable as it pleases.
Unfortunately, we can patch, polyfill, or workaround, these primitives but
we cannot change their intrinsic nature and limitations defined by web
standards. However, there are various solutions for working around such
limitations. Please read our [web workers](..//user-guide/workers/)
section to learn more.

### Borrowed proxy

Expand Down
58 changes: 30 additions & 28 deletions docs/user-guide/dom.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,39 +99,45 @@ There are three core concepts to remember:
The `find` API uses exactly the [same queries](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Locating_DOM_elements_using_selectors)
as those used by native browser methods like `qurerySelector` or
`querySelectorAll`.
* Use classes in the `pyscript.web.elements` namespace to create and organise
* Use classes in the `pyscript.web` namespace to create and organise
new elements on the web page.
* Collections of elements allow you to access and change attributes en-mass.
Such collections are returned from `find` queries and are also used for the
[children](https://developer.mozilla.org/en-US/docs/Web/API/Element/children)
of an element.

You have several options for accessing the content of the page (i.e. the DOM),
and these can be found in the `pyscript.web.dom` object. The `head` and `body`
attributes reference the page's head and body. Furthermore, the `find` method
can be used to return collections of elements matching your CSS query. Finally,
all elements have a `find` method that searches within their children for
elements matching your CSS query.
You have several options for accessing the content of the page, and these are
all found in the `pyscript.web.page` object. The `html`, `head` and `body`
attributes reference the page's top-level html, head and body. As a convenience
the `page`'s `title` attribute can be used to get and set the web page's title
(usually shown in the browser's tab). The `append` method is a shortcut for
adding content to the page's `body`. Whereas, the `find` method is used to
return collections of elements matching a CSS query. You may also shortcut
`find` via a CSS query in square brackets. Finally, all elements have a `find`
method that searches within their children for elements matching your CSS
query.

```python
from pyscript.web import dom
from pyscript.web import page


# Print all the child elements of the document's head.
print(dom.head.children)
print(page.head.children)
# Find all the paragraphs in the DOM.
paragraphs = dom.find("p")
paragraphs = page.find("p")
# Or use square brackets.
paragraphs = page["p"]
```

The object returned from a query, or used as a reference to an element's
children is iterable:

```python
from pyscript.web import dom
from pyscript.web import page


# Get all the paragraphs in the DOM.
paragraphs = dom.find("p")
paragraphs = page["p"]

# Print the inner html of each paragraph.
for p in paragraphs:
Expand All @@ -141,38 +147,36 @@ for p in paragraphs:
Alternatively, it is also indexable / sliceable:

```python
from pyscript.web import dom
from pyscript.web import page


# Get an ElementCollection of all the paragraphs in the DOM
paragraphs = dom.find("p")
paragraphs = page["p"]

# Only the final two paragraphs.
for p in paragraphs[-2:]:
print(p.html)
```

It also makes available the following read and writable attributes related to
all contained elements:
You have access to all the standard attributes related to HTML elements (for
example, the `innerHTML` or `value`), along with a couple of convenient ways
to interact with classes and CSS styles:

* `classes` - the list of classes associated with the elements.
* `innerHTML` - the innerHTML of each element.
* `style` - a dictionary like object for interacting with CSS style rules.
* `value` - the `value` attribute associated with each element.

For example, to continue the example above, `paragraphs.innerHTML` will return
a list of all the values of the `innerHTML` attribute on each contained
element. Alternatively, set an attribute for all elements contained in the
collection like this: `paragraphs.style["background-color"] = "blue"`.

It's possible to create new elements to add to the DOM:
It's possible to create new elements to add to the page:

```python
from pyscript.web import dom
from pyscript.web.elements import *
from pyscript.web import page, div, select, option, button, span, br


dom.body.append(
page.append(
div(
div("Hello!", classes="a-css-class", id="hello"),
select(
Expand Down Expand Up @@ -206,7 +210,7 @@ dom.body.append(
```

This example demonstrates a declaritive way to add elements to the body of the
DOM. Notice how the first (unnamed) arguments to an element are its children.
page. Notice how the first (unnamed) arguments to an element are its children.
The named arguments (such as `id`, `classes` and `style`) refer to attributes
of the underlying HTML element. If you'd rather be explicit about the children
of an element, you can always pass in a list of such elements as the named
Expand All @@ -216,9 +220,7 @@ Of course, you can achieve similar results in an imperative style of
programming:

```python
from pyscript.web import dom
from pyscript.web.elements import *

from pyscript.web import page, div, p


my_div = div()
Expand All @@ -238,10 +240,10 @@ element references from `pyscript.web`:

```python
from pyscript import when
from pyscript.web import dom
from pyscript.web import page


btn = dom.find("#my-button")
btn = page["#my-button"]


@when("click", btn)
Expand Down
13 changes: 9 additions & 4 deletions docs/user-guide/first-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ CSS:
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- PyScript CSS -->
<link rel="stylesheet" href="https://pyscript.net/releases/2024.7.1/core.css">
<link rel="stylesheet" href="https://pyscript.net/releases/2024.8.1/core.css">
<!-- This script tag bootstraps PyScript -->
<script type="module" src="https://pyscript.net/releases/2024.7.1/core.js"></script>
<script type="module" src="https://pyscript.net/releases/2024.8.1/core.js"></script>
</head>
<body>
<!-- your code goes here... -->
Expand Down Expand Up @@ -80,13 +80,18 @@ attributes:
* `worker` - a flag to indicate your Python code is to be run on a
[web worker](workers.md) instead of the "main thread" that looks after the user
interface.
* `target` - The id or selector of the element where calls to
* `target` - the id or selector of the element where calls to
[`display()`](../../api/#pyscriptdisplay) should write their values.
* `terminal` - A traditional [terminal](terminal.md) is shown on the page.
* `terminal` - a traditional [terminal](terminal.md) is shown on the page.
As with conventional Python, `print` statements output here. **If the
`worker` flag is set the terminal becomes interactive** (e.g. use
the `input` statement to gather characters typed into the terminal by the
user).
* `service-worker` - an optional attribute that allows you to slot in a custom
[service worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
(such as `mini-coi.js`) to fix header related problems that limit the use
of web workers. This rather technical requirement is fully explained in
the [section on web workers](../workers/#option-2-service-worker-attribute).

!!! warning

Expand Down
10 changes: 5 additions & 5 deletions docs/user-guide/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ For example, this will work because all references are contained within the
registered function:

```js
import { hooks } from "https://pyscript.net/releases/2024.7.1/core.js";
import { hooks } from "https://pyscript.net/releases/2024.8.1/core.js";

hooks.worker.onReady.add(() => {
// NOT suggested, just an example!
Expand All @@ -113,7 +113,7 @@ hooks.worker.onReady.add(() => {
However, due to the outer reference to the variable `i`, this will fail:

```js
import { hooks } from "https://pyscript.net/releases/2024.7.1/core.js";
import { hooks } from "https://pyscript.net/releases/2024.8.1/core.js";

// NO NO NO NO NO! ☠️
let i = 0;
Expand Down Expand Up @@ -146,7 +146,7 @@ the page.

```js title="log.js - a plugin that simply logs to the console."
// import the hooks from PyScript first...
import { hooks } from "https://pyscript.net/releases/2024.7.1/core.js";
import { hooks } from "https://pyscript.net/releases/2024.8.1/core.js";

// The `hooks.main` attribute defines plugins that run on the main thread.
hooks.main.onReady.add((wrap, element) => {
Expand Down Expand Up @@ -196,8 +196,8 @@ hooks.worker.onAfterRun.add(() => {
<!-- JS plugins should be available before PyScript bootstraps -->
<script type="module" src="./log.js"></script>
<!-- PyScript -->
<link rel="stylesheet" href="https://pyscript.net/releases/2024.7.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.7.1/core.js"></script>
<link rel="stylesheet" href="https://pyscript.net/releases/2024.8.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.8.1/core.js"></script>
</head>
<body>
<script type="mpy">
Expand Down
Loading

0 comments on commit ea0c9a0

Please sign in to comment.