Compare commits

...

8 Commits

Author SHA1 Message Date
Peter W
39725c1384 Use latest instead 2023-11-21 11:11:57 -06:00
Peter W
48c5158b46 Fix 404 in main README.md 2023-11-21 11:09:48 -06:00
Antonio Cuni
4b89c84692 use UTC time (#1859) 2023-11-15 16:00:09 +01:00
Antonio Cuni
df68449b82 Improve README and and mention the community calls (#1858)
Improve the readme in two ways:

- remove the mention to <py-repl>, and shows a quick summary of the various ways of running Python code

- add a link to the google calendar which contains the community calls
2023-11-15 12:10:52 +01:00
Andrea Giammarchi
48e3383f66 Fix #1841 - Provide a better error when input is used (#1857) 2023-11-14 15:25:17 +01:00
Fabio Pliger
e750fa7393 Add Deprecation message when loading from latest (#1848)
* add tests to verify if we show an error banner when users load from latest

* add deprecation manager to take care of showing a notification in case script src is being loaded from latest

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* make sure deprecation warning also register onWorker

* restore tests for banner in worker

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add a wait selector when testing banner since worker seems to take too long to render in CI

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix test_deprecate_loading_scripts_from_latest: I think that the
previous failure was because we actually TRIED to execute the js from
latest/core.js and it conflicted with our local copy.

But to trigger the warning is enough to have a script pointing to
pyscript.net/latest, there is no need to execute it: modify it with
type="ignore-me" and an URL which doesn't exist.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Antonio Cuni <anto.cuni@gmail.com>
2023-11-10 08:18:30 -08:00
Fabio Pliger
5a15199a3a Fix Click test example (#1849)
* fix type in div id

* add types
2023-11-08 11:09:15 -08:00
Fabio Pliger
1801472fc4 Remove when from pydom (#1850)
* bye bye when

* fix create to drop parent and actually pass the other arguments through

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update types

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-08 09:28:58 -08:00
11 changed files with 137 additions and 20 deletions

View File

@@ -4,9 +4,9 @@
### Summary ### Summary
PyScript is a framework that allows users to create rich Python applications in the browser using HTML's interface and the power of [Pyodide](https://pyodide.org/en/stable/), [WASM](https://webassembly.org/), and modern web technologies. PyScript is a framework that allows users to create rich Python applications in the browser using HTML's interface and the power of [Pyodide](https://pyodide.org/en/stable/), [MicroPython](https://micropython.org/) and [WASM](https://webassembly.org/), and modern web technologies.
To get started see the [getting started tutorial](docs/tutorials/getting-started.md). To get started see the [getting started tutorial](https://pyscript.github.io/docs/latest/beginning-pyscript/).
For examples see [here](examples). For examples see [here](examples).
@@ -16,7 +16,7 @@ PyScript is a meta project that aims to combine multiple open technologies into
## Try PyScript ## Try PyScript
To try PyScript, import the appropriate pyscript files into the `<head>` tag of your html page with: To try PyScript, import the appropriate pyscript files into the `<head>` tag of your html page:
```html ```html
<head> <head>
@@ -29,14 +29,25 @@ To try PyScript, import the appropriate pyscript files into the `<head>` tag of
src="https://pyscript.net/releases/2023.11.1/core.js" src="https://pyscript.net/releases/2023.11.1/core.js"
></script> ></script>
</head> </head>
<body>
<script type="py" terminal>
from pyscript import display
display("Hello World!") # this goes to the DOM
print("Hello terminal") # this goes to the terminal
</script>
</body>
``` ```
You can then use PyScript components in your html page. PyScript currently implements the following elements: You can then use PyScript components in your html page. PyScript currently offers various ways of running Python code:
- `<py-script>`: can be used to define python code that is executable within the web page. The element itself is not rendered to the page and is only used to add logic - `<script type="py">`: can be used to define python code that is executable within the web page.
- `<py-repl>`: creates a REPL component that is rendered to the page as a code editor and allows users to write executable code - `<script type="py" src="hello.py">`: same as above, but the python source is fetched from the given URL.
- `<script type="py" terminal>`: same as above, but also creates a terminal where to display stdout and stderr (e.g., the output of `print()`); `input()` does not work.
- `<script type="py" terminal worker>`: run Python inside a web worker: the terminal if fully functional and `input()` works.
- `<py-script>`: same as `<script type="py">`, but it is not recommended because if the code contains HTML tags, they could be parsed wrongly.
- `<script type="mpy">`: same as above but use MicroPython instead of Python.
Check out the [the examples directory](examples) folder for more examples on how to use it, all you need to do is open them in Chrome. Check out the [official docs](https://docs.pyscript.net) for more detailed documentation.
## How to Contribute ## How to Contribute
@@ -44,6 +55,17 @@ Read the [contributing guide](CONTRIBUTING.md) to learn about our development pr
Check out the [developing process](https://docs.pyscript.net/latest/contributing) documentation for more information on how to setup your development environment. Check out the [developing process](https://docs.pyscript.net/latest/contributing) documentation for more information on how to setup your development environment.
## Community calls and events
Every Tuesday at 15:30 UTC there is the _PyScript Community Call_ on zoom, where we can talk about PyScript development in the open. Most of the maintainers regularly participate in the call, and everybody is welcome to join.
Every other Thursday at 16:00 UTC there is the _PyScript FUN_ call: this is a call in which everybody is encouraged to show what they did with PyScript.
For more details on how to join the calls and up to date schedule, consult the official calendar:
- [Google calendar](https://calendar.google.com/calendar/u/0/embed?src=d3afdd81f9c132a8c8f3290f5cc5966adebdf61017fca784eef0f6be9fd519e0@group.calendar.google.com&ctz=UTC) in UTC time;
- [iCal format](https://calendar.google.com/calendar/ical/d3afdd81f9c132a8c8f3290f5cc5966adebdf61017fca784eef0f6be9fd519e0%40group.calendar.google.com/public/basic.ics).
## Resources ## Resources
- [Official docs](https://docs.pyscript.net) - [Official docs](https://docs.pyscript.net)

View File

@@ -45,6 +45,19 @@ export const createFunction = (self, name) => {
const SetFunction = typedSet({ typeof: "function" }); const SetFunction = typedSet({ typeof: "function" });
const SetString = typedSet({ typeof: "string" }); const SetString = typedSet({ typeof: "string" });
const inputFailure = `
import builtins
def input(prompt=""):
raise Exception("\\n ".join([
"input() doesn't work when PyScript runs in the main thread.",
"Consider using the worker attribute: https://docs.pyscript.net/2023.11.1/user-guide/workers/"
]))
builtins.input = input
del builtins
del input
`;
export const hooks = { export const hooks = {
main: { main: {
/** @type {Set<function>} */ /** @type {Set<function>} */
@@ -60,7 +73,7 @@ export const hooks = {
/** @type {Set<function>} */ /** @type {Set<function>} */
onAfterRunAsync: new SetFunction(), onAfterRunAsync: new SetFunction(),
/** @type {Set<string>} */ /** @type {Set<string>} */
codeBeforeRun: new SetString(), codeBeforeRun: new SetString([inputFailure]),
/** @type {Set<string>} */ /** @type {Set<string>} */
codeBeforeRunAsync: new SetString(), codeBeforeRunAsync: new SetString(),
/** @type {Set<string>} */ /** @type {Set<string>} */

View File

@@ -0,0 +1,27 @@
// PyScript Derepcations Plugin
import { hooks } from "../core.js";
import { notify } from "./error.js";
// react lazily on PyScript bootstrap
hooks.main.onReady.add(checkDeprecations);
hooks.main.onWorker.add(checkDeprecations);
/**
* Check that there are no scripts loading from pyscript.net/latest
*/
function checkDeprecations() {
const scripts = document.querySelectorAll("script");
for (const script of scripts) checkLoadingScriptsFromLatest(script.src);
}
/**
* Check if src being loaded from pyscript.net/latest and display a notification if true
* * @param {string} src
*/
function checkLoadingScriptsFromLatest(src) {
if (/\/pyscript\.net\/latest/.test(src)) {
notify(
"Loading scripts from latest is deprecated and will be removed soon. Please use a specific version instead.",
);
}
}

View File

@@ -6,8 +6,6 @@ from typing import Any
from pyodide.ffi import JsProxy from pyodide.ffi import JsProxy
from pyscript import display, document, window from pyscript import display, document, window
# from pyscript import when as _when
alert = window.alert alert = window.alert
@@ -177,9 +175,6 @@ class Element(BaseElement):
def show_me(self): def show_me(self):
self._js.scrollIntoView() self._js.scrollIntoView()
def when(self, event, handler):
document.when(event, selector=self)(handler)
class StyleProxy(dict): class StyleProxy(dict):
def __init__(self, element: Element) -> None: def __init__(self, element: Element) -> None:
@@ -319,8 +314,8 @@ class PyDom(BaseElement):
self.body = Element(document.body) self.body = Element(document.body)
self.head = Element(document.head) self.head = Element(document.head)
def create(self, type_, parent=None, classes=None, html=None): def create(self, type_, classes=None, html=None):
return super().create(type_, is_child=False) return super().create(type_, is_child=False, classes=classes, html=html)
def __getitem__(self, key): def __getitem__(self, key):
if isinstance(key, int): if isinstance(key, int):

View File

@@ -17,7 +17,7 @@
def on_click(event): def on_click(event):
print(f"Hello from Python! {dt.now()}") print(f"Hello from Python! {dt.now()}")
display(f"Hello from Python! {dt.now()}", append=False, target='eresult') display(f"Hello from Python! {dt.now()}", append=False, target='result')
add_event_listener(element, "click", on_click) add_event_listener(element, "click", on_click)
</script> </script>

View File

@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyScript Next</title>
<script>
addEventListener("py:ready", console.log);
</script>
<link rel="stylesheet" href="../dist/core.css">
<script type="module" src="../dist/core.js"></script>
</head>
<body>
<py-script>
input("what's your name?")
</py-script>
<mpy-script>
input("what's your name?")
</mpy-script>
</body>
</html>

View File

@@ -545,7 +545,9 @@ class PyScriptTest:
- wait until pyscript has been fully loaded - wait until pyscript has been fully loaded
""" """
doc = self._pyscript_format( doc = self._pyscript_format(
snippet, execution_thread=self.execution_thread, extra_head=extra_head snippet,
execution_thread=self.execution_thread,
extra_head=extra_head,
) )
if not wait_for_pyscript and timeout is not None: if not wait_for_pyscript and timeout is not None:
raise ValueError("Cannot set a timeout if wait_for_pyscript=False") raise ValueError("Cannot set a timeout if wait_for_pyscript=False")

View File

@@ -93,6 +93,19 @@ class TestBasic(PyScriptTest):
) )
assert self.console.log.lines[-1] == "hello pyscript" assert self.console.log.lines[-1] == "hello pyscript"
@only_main
def test_input_exception(self):
self.pyscript_run(
"""
<script type="py">
input("what's your name?")
</script>
"""
)
self.check_py_errors(
"Exception: input() doesn't work when PyScript runs in the main thread."
)
@skip_worker("NEXT: exceptions should be displayed in the DOM") @skip_worker("NEXT: exceptions should be displayed in the DOM")
def test_python_exception(self): def test_python_exception(self):
self.pyscript_run( self.pyscript_run(

View File

@@ -1,13 +1,35 @@
import pytest import pytest
from .support import PyScriptTest from .support import PyScriptTest, skip_worker
pytest.skip(reason="NEXT: Restore the banner", allow_module_level=True)
class TestWarningsAndBanners(PyScriptTest): class TestWarningsAndBanners(PyScriptTest):
# Test the behavior of generated warning banners # Test the behavior of generated warning banners
def test_deprecate_loading_scripts_from_latest(self):
# Use a script tag with an invalid output attribute to generate a warning, but only one
self.pyscript_run(
"""
<script type="py">
print("whatever..")
</script>
""",
extra_head='<script type="ignore-me" src="https://pyscript.net/latest/any-path-triggers-the-warning-anyway.js"></script>',
)
# wait for the banner to appear (we could have a page.locater call but for some reason
# the worker takes to long to render on CI, since it's a test we can afford 2 calls)
loc = self.page.wait_for_selector(".py-error")
assert (
loc.inner_text()
== "Loading scripts from latest is deprecated and will be removed soon. Please use a specific version instead."
)
# Only one banner should appear
loc = self.page.locator(".py-error")
assert loc.count() == 1
@pytest.mark.skip("NEXT: To check if behaviour is consistent with classic")
def test_create_singular_warning(self): def test_create_singular_warning(self):
# Use a script tag with an invalid output attribute to generate a warning, but only one # Use a script tag with an invalid output attribute to generate a warning, but only one
self.pyscript_run( self.pyscript_run(

View File

@@ -1,4 +1,5 @@
declare const _default: { declare const _default: {
"deprecations-manager": () => Promise<typeof import("./plugins/deprecations-manager.js")>;
error: () => Promise<typeof import("./plugins/error.js")>; error: () => Promise<typeof import("./plugins/error.js")>;
"py-terminal": () => Promise<typeof import("./plugins/py-terminal.js")>; "py-terminal": () => Promise<typeof import("./plugins/py-terminal.js")>;
}; };

View File

@@ -0,0 +1 @@
export {};