diff --git a/docs/example_md.md b/docs/example_md.md
index d03be05..b646589 100644
--- a/docs/example_md.md
+++ b/docs/example_md.md
@@ -8,30 +8,23 @@ py-config:
# Example with MyST
-## `py-repl` and `py-terminal`
+## `py-editor` and `py-terminal`
We can create a REPL which will output to a `div` and print `stdout` to a terminal with:
````md
-```{py-repl}
-:output: replOutput
+```{py-editor}
print("hallo world")
import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
plt.gcf()
```
-
-
-
-```{py-terminal}
-```
````
Press `shift+enter` to run the code.
-```{py-repl}
-:output: replOutput
+```{py-editor}
print("hallo world")
import matplotlib.pyplot as plt
@@ -39,11 +32,6 @@ plt.plot([1, 2, 3])
plt.gcf()
```
-
-
-```{py-terminal}
-```
-
## `py-script` application
Here is a simple application to replace "a" with "b", using the `py-script` directive:
diff --git a/docs/example_rst.rst b/docs/example_rst.rst
index 71e6f7c..92ddc57 100644
--- a/docs/example_rst.rst
+++ b/docs/example_rst.rst
@@ -8,42 +8,80 @@
Example with RST
================
-`py-repl` and `py-terminal`
-----------------------------
+`py-editor` and `py-terminal`
+-----------------------------
-We can create a REPL which will output to a `div` and print `stdout` to a terminal with:
+We can create an editor cell which will print its `stdout`:
.. code-block:: restructuredtext
- .. py-repl::
- :output: replOutput
+ .. py-editor::
print("hallo world")
import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
plt.gcf()
- .. raw:: html
-
-
-
- .. py-terminal::
-
Press `shift+enter` to run the code.
-.. py-repl::
- :output: replOutput
+.. py-editor::
print("hallo world")
import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
plt.gcf()
-.. raw:: html
+By default, each editor uses a separate copy of the Python interpreter. Code blocks with the same `env` (environment) share a copy of the Python interpreter:
+
+.. code-block:: restructuredtext
+
+ .. py-editor::
+ :env: one
+
+ x = 1
+
+ .. py-editor::
+ :env: one
+
+ print(x)
+
+ .. py-editor::
+ :env: two
+
+ print(x) # Error: x is not defined
+
+Add the `setup` option to an editor tag in a given environment to include code that will run just before the first time the visible code in a block runs in that environment. Code in a `setup` block is invisible to the user. This is useful for setting up variables, imports, etc without cluttering up the editor cells.
+
+.. code-block:: restructuredtext
+
+ .. py-editor::
+ :env: one
+ :setup:
+
+ # This code is not visible on the page
+ from datetime import datetime
+
+ .. py-editor::
+ :env: one
+
+ print(datetime.now())
+
+Use the `config` option to specify the url of a `PyScript Configuration File `_:
+
+.. code-block:: toml
+
+ # config.toml
+ packages = ['numpy', 'pandas']
+
+.. code-block:: restructuredtext
+
+ .. py-editor::
+ :config: config.toml
-
+ import numpy as np
+ import pandas as pd
-.. py-terminal::
+ s = pd.Series([1, 3, 5, np.nan, 6, 8])
`py-script` application
-----------------------
diff --git a/src/sphinx_pyscript.py b/sphinx_pyscript/__init__.py
similarity index 60%
rename from src/sphinx_pyscript.py
rename to sphinx_pyscript/__init__.py
index 0dfa4dc..f62ac3b 100644
--- a/src/sphinx_pyscript.py
+++ b/sphinx_pyscript/__init__.py
@@ -1,6 +1,6 @@
"""A sphinx extension for adding pyscript to a page"""
-__version__ = "0.1.0"
+__version__ = "0.2.0"
import json
from pathlib import Path
@@ -10,23 +10,29 @@
from docutils.parsers.rst import directives
from sphinx.application import Sphinx
from sphinx.util.docutils import SphinxDirective
+from sphinx.util.fileutil import copy_asset_file
from sphinx.util.logging import getLogger
+DEFAULT_VERSION = "2024.5.2"
+
def setup(app: Sphinx):
"""Setup the extension"""
app.add_config_value(
- "pyscript_js", "https://pyscript.net/releases/2022.12.1/pyscript.js", "env"
+ "pyscript_js", f"https://pyscript.net/releases/{DEFAULT_VERSION}/core.js", "env"
)
app.add_config_value(
- "pyscript_css", "https://pyscript.net/releases/2022.12.1/pyscript.css", "env"
+ "pyscript_css",
+ f"https://pyscript.net/releases/{DEFAULT_VERSION}/core.css",
+ "env",
)
app.add_directive("py-config", PyConfig)
app.add_directive("py-script", PyScript)
- app.add_directive("py-repl", PyRepl)
+ app.add_directive("py-editor", PyEditor)
app.add_directive("py-terminal", PyTerminal)
app.connect("doctree-read", doctree_read)
app.connect("html-page-context", add_html_context)
+ app.connect("env-updated", copy_asset_files)
return {"version": __version__, "parallel_read_safe": True}
@@ -72,44 +78,66 @@ def run(self):
code = "\n".join(self.content)
else:
raise self.error("Must provide either content or the 'file' option")
- return [nodes.raw("", f"\n{code}\n\n", format="html")]
+ return [
+ nodes.raw("", f"\n", format="html")
+ ]
-class PyRepl(SphinxDirective):
- """Add a py-repl tag"""
+class PyEditor(SphinxDirective):
+ """Add a py-editor tag"""
has_content = True
+
+ """
+ Notes on options. See https://docs.pyscript.net/2024.5.2/user-guide/editor/ for details
+ env: The name of a particular instance of the CPython interpreter. Cells with the same 'env' share an interpreter
+ setup: designates a 'setup' tag
+ config: The URL of a PyScript configuration file (TOML or JSON), or an inline configuration
+ """
option_spec = {
- "auto-generate": directives.flag,
- "output": directives.unchanged,
+ "env": directives.unchanged,
+ "setup": directives.flag,
+ "config": directives.unchanged,
}
def run(self):
- """Add the py-repl tag"""
+ """Add the py-editor tag"""
attrs = ""
code = ""
- if "auto-generate" in self.options:
- attrs += ' auto-generate="true"'
- if "output" in self.options:
- attrs += f' output="{self.options["output"]}"'
+ if "env" in self.options:
+ attrs += f' env="{self.options["""env"""]}"'
+ if "config" in self.options:
+ attrs += f' config="{self.options["""config"""]}"'
+ if "setup" in self.options:
+ attrs += "setup"
if self.content:
code = "\n".join(self.content)
- return [nodes.raw("", f"\n{code}\n\n", format="html")]
+ return [
+ nodes.raw(
+ "",
+ f'\n',
+ format="html",
+ )
+ ]
class PyTerminal(SphinxDirective):
"""Add a py-terminal tag"""
option_spec = {
- "auto": directives.flag,
+ "worker": directives.flag,
}
def run(self):
"""Add the py-terminal tag"""
attrs = ""
- if "auto" in self.options:
- attrs += " auto"
- return [nodes.raw("", f"\n", format="html")]
+ if "worker" in self.options:
+ attrs += " worker"
+ return [
+ nodes.raw(
+ "", f"\n", format="html"
+ )
+ ]
def add_html_context(
@@ -117,7 +145,8 @@ def add_html_context(
):
"""Add extra variables to the HTML template context."""
if doctree and "pyscript" in doctree:
- app.add_js_file(app.config.pyscript_js, loading_method="defer")
+ app.add_js_file(app.config.pyscript_js, type="module")
+ app.add_js_file("../mini-coi.js")
app.add_css_file(app.config.pyscript_css)
@@ -142,3 +171,10 @@ def doctree_read(app: Sphinx, doctree: nodes.document):
format="html",
)
)
+
+
+def copy_asset_files(app, _):
+ if app.builder.format == "html":
+ custom_file = (Path(__file__).parent / "mini-coi.js").absolute()
+ static_dir = (Path(app.builder.outdir)).absolute()
+ copy_asset_file(str(custom_file), str(static_dir))
diff --git a/sphinx_pyscript/mini-coi.js b/sphinx_pyscript/mini-coi.js
new file mode 100644
index 0000000..128f073
--- /dev/null
+++ b/sphinx_pyscript/mini-coi.js
@@ -0,0 +1,31 @@
+/*! This script installs a service worker to set the COOP, COEP, and CORP headers required
+to use SharedArrayBuffer in the browser. This is required to use py-editor cells.
+See https://docs.pyscript.net/2024.5.2/user-guide/workers/ for details */
+/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */
+/*! mini-coi - Andrea Giammarchi and contributors, licensed under MIT */
+(({ document: d, navigator: { serviceWorker: s } }) => {
+ if (d) {
+ const { currentScript: c } = d;
+ s.register(c.src, { scope: c.getAttribute('scope') || '.' }).then(r => {
+ r.addEventListener('updatefound', () => location.reload());
+ if (r.active && !s.controller) location.reload();
+ });
+ }
+ else {
+ addEventListener('install', () => skipWaiting());
+ addEventListener('activate', e => e.waitUntil(clients.claim()));
+ addEventListener('fetch', e => {
+ const { request: r } = e;
+ if (r.cache === 'only-if-cached' && r.mode !== 'same-origin') return;
+ e.respondWith(fetch(r).then(r => {
+ const { body, status, statusText } = r;
+ if (!status || status > 399) return r;
+ const h = new Headers(r.headers);
+ h.set('Cross-Origin-Opener-Policy', 'same-origin');
+ h.set('Cross-Origin-Embedder-Policy', 'require-corp');
+ h.set('Cross-Origin-Resource-Policy', 'cross-origin');
+ return new Response(body, { status, statusText, headers: h });
+ }));
+ });
+ }
+ })(self);
diff --git a/tests/test_basic.py b/tests/test_basic.py
index 98128f7..7eb2072 100644
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -14,8 +14,7 @@ def test_basic(sphinx_doctree: CreateDoctree):
splashscreen:
autoclose: true
-.. py-repl::
- :output: replOutput
+.. py-editor::
.. py-terminal::
@@ -33,15 +32,15 @@ def test_basic(sphinx_doctree: CreateDoctree):
Test
-
+
-
+
-
+
{