mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-21 11:15:36 -05:00
Compare commits
162 Commits
2023.03.1
...
fpliger/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db66bb71e8 | ||
|
|
fc89d157ce | ||
|
|
4658b6d9a1 | ||
|
|
8cb5294f2a | ||
|
|
f226506856 | ||
|
|
c739e23cc5 | ||
|
|
5f93eb24bb | ||
|
|
da5569871e | ||
|
|
e33095249c | ||
|
|
efcd872ece | ||
|
|
829e4f89f1 | ||
|
|
0e710461fe | ||
|
|
bed56df606 | ||
|
|
78aa257a21 | ||
|
|
a43dab9850 | ||
|
|
e63ce9b685 | ||
|
|
93e4e485ff | ||
|
|
df7be28c18 | ||
|
|
da3b43abdd | ||
|
|
4cc9647dc6 | ||
|
|
74cd7c840a | ||
|
|
0f2deeb71a | ||
|
|
93539c9b5a | ||
|
|
e48e6276e1 | ||
|
|
75a57a49f5 | ||
|
|
8a1db288fc | ||
|
|
84dcde188b | ||
|
|
27c91e9703 | ||
|
|
b5a0cd4057 | ||
|
|
77d8fe3562 | ||
|
|
a484aff457 | ||
|
|
c96f5912df | ||
|
|
8a01a56e51 | ||
|
|
2774e49ab9 | ||
|
|
26e7a54f1f | ||
|
|
f0e69cbc36 | ||
|
|
413428f535 | ||
|
|
0c54036466 | ||
|
|
2555833831 | ||
|
|
7e0aceced1 | ||
|
|
77234f6df3 | ||
|
|
45af96aad4 | ||
|
|
184d29055e | ||
|
|
9e73181816 | ||
|
|
0b0e03456c | ||
|
|
c6b5ce7f55 | ||
|
|
a14e701be4 | ||
|
|
7813c3f03f | ||
|
|
3a3cb7b11d | ||
|
|
d7b0731385 | ||
|
|
df8973736f | ||
|
|
9121071ba3 | ||
|
|
bf6470c046 | ||
|
|
3b7099cd3d | ||
|
|
f6dfc5361e | ||
|
|
0a7e1ce0d7 | ||
|
|
d6b1c393f6 | ||
|
|
bccd5e3750 | ||
|
|
6df5905b2b | ||
|
|
6284c02032 | ||
|
|
db27d52352 | ||
|
|
8ba28989fb | ||
|
|
da544929ac | ||
|
|
bb364b0524 | ||
|
|
818614b798 | ||
|
|
50b1a1d7c5 | ||
|
|
7d3b792a79 | ||
|
|
af72e232c3 | ||
|
|
0cdbfbeb30 | ||
|
|
339e40063a | ||
|
|
4467898473 | ||
|
|
17d16b987f | ||
|
|
8e86daac71 | ||
|
|
856720da49 | ||
|
|
8f2c150d1e | ||
|
|
7d8b4c980a | ||
|
|
932756c7a0 | ||
|
|
538aac9a28 | ||
|
|
856bf8f5fb | ||
|
|
e1758ae2e2 | ||
|
|
61b3154461 | ||
|
|
fb9b30d144 | ||
|
|
b0df96b13f | ||
|
|
a469062a32 | ||
|
|
89d5d5c7db | ||
|
|
b8c2d6b05d | ||
|
|
b247864414 | ||
|
|
d3bcd87cfa | ||
|
|
82e5b64bad | ||
|
|
73e0271c23 | ||
|
|
a2dabee0e9 | ||
|
|
6a27c6d9f2 | ||
|
|
213ced0c7f | ||
|
|
5086c23d47 | ||
|
|
ee345a5206 | ||
|
|
f74cddc3b1 | ||
|
|
5b986b8b26 | ||
|
|
14887b9814 | ||
|
|
ecc40315b3 | ||
|
|
e7aed7fcf0 | ||
|
|
cd1aa948f9 | ||
|
|
82613d016a | ||
|
|
3a66be585f | ||
|
|
0a4e36ae09 | ||
|
|
92643539cf | ||
|
|
a1281d1331 | ||
|
|
074ca0ef8f | ||
|
|
464a9633dc | ||
|
|
fc2d91c5bb | ||
|
|
d68169bffb | ||
|
|
7efdb04e1e | ||
|
|
0155e122fd | ||
|
|
eb03f16a77 | ||
|
|
5ac39641ab | ||
|
|
8d1e48e400 | ||
|
|
0021ccb49f | ||
|
|
8590c7e5b8 | ||
|
|
8c5475f78f | ||
|
|
dfa116eb70 | ||
|
|
3a9fd3c074 | ||
|
|
5a92ef3c11 | ||
|
|
d3902f5c93 | ||
|
|
c886f887ae | ||
|
|
fc5089ac59 | ||
|
|
e3602f464b | ||
|
|
f3db6a339c | ||
|
|
c05195c045 | ||
|
|
af981fc719 | ||
|
|
088a264910 | ||
|
|
d7e80ad51b | ||
|
|
b53ddd401f | ||
|
|
e9122bca9d | ||
|
|
b61e8435d1 | ||
|
|
146afb6532 | ||
|
|
854e9d1378 | ||
|
|
689878ce32 | ||
|
|
d7ab177cc5 | ||
|
|
f4c6093c47 | ||
|
|
9fedfe3699 | ||
|
|
26f07246e1 | ||
|
|
3ae4b3c4de | ||
|
|
c8f9f16791 | ||
|
|
88f0738500 | ||
|
|
03c79d5f2f | ||
|
|
e7c3b7bcfe | ||
|
|
c8becca044 | ||
|
|
543a27271f | ||
|
|
a62aba83a0 | ||
|
|
53c6cf5f45 | ||
|
|
89842e20da | ||
|
|
ef793aecf3 | ||
|
|
51d51409d3 | ||
|
|
371b5eac45 | ||
|
|
5319bd13d5 | ||
|
|
e10d055453 | ||
|
|
716254e655 | ||
|
|
4c00b1683f | ||
|
|
37c9db09c6 | ||
|
|
653e2c9be4 | ||
|
|
a2a9613da1 | ||
|
|
e8d92d0d34 | ||
|
|
755b98a8c0 |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Feature Proposals
|
||||
url: https://github.com/pyscript/pyscript/discussions/new?category=proposals
|
||||
|
||||
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
## Description
|
||||
|
||||
<!--Please describe the changes in your pull request in few words here. -->
|
||||
|
||||
## Changes
|
||||
|
||||
<!-- List the changes done to fix a bug or introduce a new feature.Please note both user-facing changes and changes to internal API's here -->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!-- Note: Only user-facing changes require a changelog entry. Internal-only API changes do not require a changelog entry. Changes in documentation do not require a changelog entry. -->
|
||||
|
||||
- [ ] All tests pass locally
|
||||
- [ ] I have updated `docs/changelog.md`
|
||||
- [ ] I have created documentation for this(if applicable)
|
||||
3
.github/workflows/build-unstable.yml
vendored
3
.github/workflows/build-unstable.yml
vendored
@@ -67,6 +67,9 @@ jobs:
|
||||
- name: Integration Tests
|
||||
run: make test-integration-parallel
|
||||
|
||||
- name: Examples Tests
|
||||
run: make test-examples
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pyscript
|
||||
|
||||
20
.github/workflows/docs-review.yml
vendored
20
.github/workflows/docs-review.yml
vendored
@@ -51,23 +51,3 @@ jobs:
|
||||
with:
|
||||
name: pyscript-docs-review-${{ github.event.number }}
|
||||
path: docs/_build/html/
|
||||
|
||||
# Deploy to S3
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
- name: Copy redirect file
|
||||
run: aws s3 cp --quiet ./docs/_build/html/_static/redirect.html s3://docs.pyscript.net/index.html
|
||||
|
||||
- name: Sync to S3
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/review/${{ github.event.number }}/
|
||||
|
||||
- name: Adding step summary
|
||||
run: |
|
||||
echo "### Review documentation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "As with any pull request, you can find the rendered documentation version for pull request ${{ github.event.number }} here:"
|
||||
echo "" >> $GITHUB_STEP_SUMMARY # this is a blank line
|
||||
echo "https://docs.pyscript.net/review/${{ github.event.number }}/" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
4
.github/workflows/docs-unstable.yml
vendored
4
.github/workflows/docs-unstable.yml
vendored
@@ -49,6 +49,10 @@ jobs:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
# Sync will only copy changed files
|
||||
- name: Sync Error
|
||||
run: aws s3 cp --quiet ./docs/_static/s3_error.html s3://docs.pyscript.net/error.html
|
||||
|
||||
# Sync will only copy changed files
|
||||
- name: Sync to S3
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/unstable/
|
||||
|
||||
74
.github/workflows/test-next.yml
vendored
Normal file
74
.github/workflows/test-next.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
name: "[CI] Test Next"
|
||||
|
||||
on:
|
||||
push: # Only run on merges into main that modify files under pyscriptjs/ and examples/
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- pyscript.core/**
|
||||
- .github/workflows/test-next.yml # Test that workflow works when changed
|
||||
|
||||
pull_request: # Run on any PR that modifies files under pyscriptjs/ and examples/
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- pyscript.core/**
|
||||
- .github/workflows/test-next.yml # Test that workflow works when changed
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
TestNext:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: pyscript.core
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
# npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
# TODO: this will likely change soon to pyscript.next
|
||||
# - name: install next deps
|
||||
# working-directory: pyscript.core
|
||||
# run: npm i; npx playwright install
|
||||
|
||||
# - name: build next
|
||||
# working-directory: pyscript.core
|
||||
# run: npm run build
|
||||
|
||||
# - name: Run next tests
|
||||
# working-directory: pyscript.core
|
||||
# run: npm run test
|
||||
|
||||
# TODO: DO we want to upload next yet?
|
||||
# - uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: pyscript
|
||||
# path: |
|
||||
# pyscriptjs/build/
|
||||
# if-no-files-found: error
|
||||
# retention-days: 7
|
||||
|
||||
# - uses: actions/upload-artifact@v3
|
||||
# if: success() || failure()
|
||||
# with:
|
||||
# name: test_results
|
||||
# path: pyscriptjs/test_results
|
||||
# if-no-files-found: error
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -73,6 +73,7 @@ instance/
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
docs/_env/
|
||||
newdocs/_env/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Check out the docs at: https://pre-commit.com/
|
||||
ci:
|
||||
skip: [eslint]
|
||||
autoupdate_schedule: monthly
|
||||
|
||||
default_stages: [commit]
|
||||
repos:
|
||||
@@ -13,19 +14,20 @@ repos:
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-json
|
||||
exclude: tsconfig.json
|
||||
exclude: tsconfig\.json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: detect-private-key
|
||||
- id: end-of-file-fixer
|
||||
exclude: \.min\.js$
|
||||
exclude: pyscript\.core/core.*|\.min\.js$
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.254
|
||||
rev: v0.0.257
|
||||
hooks:
|
||||
- id: ruff
|
||||
exclude: pyscript\.core/test|pyscript\.core/src/display.py
|
||||
args: [--fix]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
@@ -34,9 +36,10 @@ repos:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.2
|
||||
rev: v2.2.4
|
||||
hooks:
|
||||
- id: codespell # See 'pyproject.toml' for args
|
||||
exclude: \.js\.map$
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
|
||||
@@ -44,16 +47,17 @@ repos:
|
||||
rev: "v3.0.0-alpha.6"
|
||||
hooks:
|
||||
- id: prettier
|
||||
exclude: pyscript\.core/test|pyscript\.core/core.*|pyscript\.core/types/|pyscript\.sw/
|
||||
args: [--tab-width, "4"]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v8.35.0
|
||||
rev: v8.36.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
files: pyscriptjs/src/.*\.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
|
||||
types: [file]
|
||||
additional_dependencies:
|
||||
- eslint@8.25.0
|
||||
- typescript@4.8.4
|
||||
- "@typescript-eslint/eslint-plugin@5.39.0"
|
||||
- "@typescript-eslint/parser@5.39.0"
|
||||
- typescript@5.0.4
|
||||
- "@typescript-eslint/eslint-plugin@5.58.0"
|
||||
- "@typescript-eslint/parser@5.58.0"
|
||||
|
||||
@@ -2,3 +2,4 @@ ISSUE_TEMPLATE
|
||||
*.min.*
|
||||
package-lock.json
|
||||
docs
|
||||
examples/panel.html
|
||||
|
||||
@@ -4,14 +4,21 @@ Thank you for wanting to contribute to the PyScript project!
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Contributing to PyScript](#contributing-to-pyscript)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [Contributing](#contributing)
|
||||
- [Reporting bugs](#reporting-bugs)
|
||||
- [Creating useful issues](#creating-useful-issues)
|
||||
- [Reporting security issues](#reporting-security-issues)
|
||||
- [Asking questions](#asking-questions)
|
||||
- [Setting up your local environment and developing](#setting-up-your-local-environment-and-developing)
|
||||
- [Developing](#developing)
|
||||
- [Rebasing changes](#rebasing-changes)
|
||||
- [Building the docs](#building-the-docs)
|
||||
- [Places to start](#places-to-start)
|
||||
- [Setting up your local environment and developing](#setting-up-your-local-environment-and-developing)
|
||||
- [Submitting a change](#submitting-a-change)
|
||||
- [License terms for contributions](#license-terms-for-contributions)
|
||||
- [Becoming a maintainer](#becoming-a-maintainer)
|
||||
- [Trademarks](#trademarks)
|
||||
@@ -43,7 +50,7 @@ If you have questions about the project, using PyScript, or anything else, pleas
|
||||
|
||||
## Places to start
|
||||
|
||||
If you would like to contribute to PyScript, but you aren't sure where to begin, here are some suggestions.
|
||||
If you would like to contribute to PyScript, but you aren't sure where to begin, here are some suggestions:
|
||||
|
||||
- **Read over the existing documentation.** Are there things missing, or could they be clearer? Make some changes/additions to those documents.
|
||||
- **Review the open issues.** Are they clear? Can you reproduce them? You can add comments, clarifications, or additions to those issues. If you think you have an idea of how to address the issue, submit a fix!
|
||||
|
||||
@@ -9,13 +9,14 @@ This document lists the Maintainers of the Project. Maintainers may be added onc
|
||||
| Philipp Rudiger | Anaconda, Inc |
|
||||
| Peter Wang | Anaconda, Inc |
|
||||
| Kevin Goldsmith | Anaconda, Inc |
|
||||
| Mariana Meireles | Anaconda, Inc |
|
||||
| Mariana Meireles | |
|
||||
| Nicholas H.Tollervey | Anaconda, Inc |
|
||||
| Madhur Tandon | Anaconda, Inc |
|
||||
| Ted Patrick | Anaconda, Inc |
|
||||
| Jeff Glass | --- |
|
||||
| Paul Everitt | --- |
|
||||
| Fabio Rosado | --- |
|
||||
| Jeff Glass | |
|
||||
| Paul Everitt | |
|
||||
| Fabio Rosado | Anaconda, Inc |
|
||||
| Andrea Giammarchi | Anaconda, Inc |
|
||||
|
||||
---
|
||||
|
||||
|
||||
4
docs/_static/examples/what-is-pyscript.html
vendored
4
docs/_static/examples/what-is-pyscript.html
vendored
@@ -3,6 +3,10 @@
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<style>
|
||||
h1 {
|
||||
color: #459db9;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
4
docs/_static/s3_error.html
vendored
Normal file
4
docs/_static/s3_error.html
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<html><head><meta http-equiv="refresh" content="5; URL='/latest/'" /></head><body>
|
||||
<h1>404 - File not found</h1>
|
||||
<p>You will be redirected to the latest documentation in 5 seconds.</p>
|
||||
</body></html>
|
||||
@@ -1,13 +1,68 @@
|
||||
# Release Notes
|
||||
|
||||
2023.01.1
|
||||
2023.XX.X
|
||||
=========
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Restored the `output` attribute of <py-script> tags to route `sys.stdout` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
|
||||
- Added a `stderr` attribute of <py-script> tags to route `sys.stderr` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
|
||||
- Added the `xterm` attribute to `py-config`. When set to `True` or `xterm`, an (output-only) [xterm.js](http://xtermjs.org/) terminal will be used in place of the default py-terminal.
|
||||
- The default version of Pyodide is now `0.23.2`. See the [Pyodide Changelog](https://pyodide.org/en/stable/project/changelog.html#version-0-23-2) for a detailed list of changes.
|
||||
- Added the `@when` decorator for attaching Python functions as event handlers
|
||||
- The `py-mount` attribute on HTML elements has been deprecated, and will be removed in a future release.
|
||||
|
||||
|
||||
### Runtime py- attributes
|
||||
|
||||
- Added logic to react to `py-*` attributes changes, removal, `py-*` attributes added to already live nodes but also `py-*` attributes added or defined via injected nodes (either appended or via `innerHTML` operations). ([#1435](https://github.com/pyscript/pyscript/pull/1435))
|
||||
|
||||
### <script type="py">
|
||||
- Added the ability to optionally use `<script type="py">`, `<script type="pyscript">` or `<script type="py-script">` instead of a `<py-script>` custom element, in order to tackle cases where the content of the `<py-script>` tag, inevitably parsed by browsers, could accidentally contain *HTML* able to break the surrounding page layout. ([#1396](https://github.com/pyscript/pyscript/pull/1396))
|
||||
|
||||
### <py-terminal>
|
||||
- Added a `docked` field and attribute for the `<py-terminal>` custom element, enabled by default when the terminal is in `auto` mode, and able to dock the terminal at the bottom of the page with auto scroll on new code execution.
|
||||
|
||||
### <py-script>
|
||||
- Restored the `output` attribute of `py-script` tags to route `sys.stdout` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
|
||||
- Added a `stderr` attribute of `py-script` tags to route `sys.stderr` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
|
||||
|
||||
### <py-repl>
|
||||
- The `output` attribute of `py-repl` tags now specifies the id of the DOM element that `sys.stdout`, `sys.stderr`, and the results of a REPL execution are written to. It no longer affects the location of calls to `display()`
|
||||
- Added a `stderr` attribute of `py-repl` tags to route `sys.stderr` to a DOM element with the given ID. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
|
||||
- Resored the `output-mode` attribute of `py-repl` tags. If `output-mode` == 'append', the DOM element where output is printed is _not_ cleared before writing new results.
|
||||
- Load code from the attribute src of py-repl and preload it into the corresponding py-repl tag by use the attribute `str` in your `py-repl` tag([#1292](https://github.com/pyscript/pyscript/pull/1292))
|
||||
|
||||
### Plugins
|
||||
- Plugins may now implement the `beforePyReplExec()` and `afterPyReplExec()` hooks, which are called immediately before and after code in a `py-repl` tag is executed. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
|
||||
|
||||
### Web worker support
|
||||
- introduced the new experimental `execution_thread` config option: if you set `execution_thread = "worker"`, the python interpreter runs inside a web worker
|
||||
- worker support is still **very** experimental: not everything works, use it at your own risk
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fixes [#1280](https://github.com/pyscript/pyscript/issues/1280), which describes the errors on the PyRepl tests related to having auto-gen tags that shouldn't be there.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
- Py-REPL tests now run on both osx and non osx OSs
|
||||
- migrated from *rollup* to *esbuild* to create artifacts
|
||||
- updated `@codemirror` dependency to its latest
|
||||
|
||||
Docs
|
||||
----
|
||||
|
||||
- Add docs for event handlers
|
||||
|
||||
2023.01.1
|
||||
=========
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
@@ -18,6 +73,7 @@ Bug fixes
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
- When adding a `py-` attribute to an element but didn't added an `id` attribute, PyScript will now generate a random ID for the element instead of throwing an error which caused the splash screen to not shutdown. ([#1122](https://github.com/pyscript/pyscript/pull/1122))
|
||||
- You can now disable the splashscreen by setting `enabled = false` in your `py-config` under the `[splashscreen]` configuration section. ([#1138](https://github.com/pyscript/pyscript/pull/1138))
|
||||
|
||||
@@ -35,3 +91,4 @@ Deprecations and Removals
|
||||
- The py-config `runtimes` to specify an interpreter has been deprecated. The `interpreters` config should be used instead. ([#1082](https://github.com/pyscript/pyscript/pull/1082))
|
||||
- The attributes `pys-onClick` and `pys-onKeyDown` have been deprecated, but the warning was only shown in the console. An alert banner will now be shown on the page if the attributes are used. They will be removed in the next release. ([#1084](https://github.com/pyscript/pyscript/pull/1084))
|
||||
- The pyscript elements `py-button`, `py-inputbox`, `py-box` and `py-title` have now completed their deprecation cycle and have been removed. ([#1084](https://github.com/pyscript/pyscript/pull/1084))
|
||||
- The attributes `pys-onClick` and `pys-onKeyDown` have been removed. Use `py-click` and `py-keydown` instead ([#1361](https://github.com/pyscript/pyscript/pull/1361))
|
||||
|
||||
@@ -19,7 +19,7 @@ import os
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "PyScript"
|
||||
copyright = "(c) 2022, Anaconda, Inc."
|
||||
copyright = "(c) 2023, Anaconda, Inc."
|
||||
author = "Anaconda, Inc."
|
||||
language = "en"
|
||||
|
||||
|
||||
@@ -28,3 +28,11 @@ showWarning(`
|
||||
</p>
|
||||
`, "html")
|
||||
```
|
||||
|
||||
## Deprecation History
|
||||
|
||||
This section tracks deprecations of specific features, both for historical record and to help the development team remember to actually remove deprecated features in future releases.
|
||||
|
||||
|Attribute/Object/Functionality|Deprecated In|Removed In|
|
||||
|-|-|-|
|
||||
|`py-mount` attribute | (Release following 2023.03.1) | -|
|
||||
|
||||
@@ -41,7 +41,7 @@ git checkout -b <your branch name>
|
||||
* Run tests before pushing the changes
|
||||
|
||||
```
|
||||
make tests
|
||||
make test
|
||||
```
|
||||
|
||||
To learn more about tests please refer to the session [Quick guide to pytest](## Quick guide to pytest).
|
||||
@@ -146,8 +146,8 @@ $ pytest test_01_basic.py -k test_pyscript_hello -s
|
||||
[ 0.00 page.goto ] pyscript_hello.html
|
||||
[ 0.01 request ] 200 - fake_server - http://fake_server/pyscript_hello.html
|
||||
...
|
||||
[ 0.17 console.info ] [py-loader] Downloading pyodide-0.22.1...
|
||||
[ 0.18 request ] 200 - CACHED - https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js
|
||||
[ 0.17 console.info ] [py-loader] Downloading pyodide-x.y.z...
|
||||
[ 0.18 request ] 200 - CACHED - https://cdn.jsdelivr.net/pyodide/vx.y.z/full/pyodide.js
|
||||
...
|
||||
[ 3.59 console.info ] [pyscript/main] PyScript page fully initialized
|
||||
[ 3.60 console.log ] hello pyscript
|
||||
@@ -190,6 +190,16 @@ $ pytest test_01_basic.py -k test_pyscript_hello -s --dev
|
||||
`--dev` implies `--headed --no-fake-server`. In addition, it also
|
||||
automatically open chrome dev tools.
|
||||
|
||||
#### To run only main thread or worker tests
|
||||
|
||||
By default, we run each test twice: one with `execution_thread = "main"` and
|
||||
one with `execution_thread = "worker"`. If you want to run only half of them,
|
||||
you can use `-m`:
|
||||
|
||||
```
|
||||
$ pytest -m main # run only the tests in the main thread
|
||||
$ pytest -m worker # ron only the tests in the web worker
|
||||
```
|
||||
|
||||
## Fake server, HTTP cache
|
||||
|
||||
@@ -207,25 +217,6 @@ If you want to temporarily disable the cache, the easiest thing is to use
|
||||
If you want to clear the cache, you can use the special option
|
||||
`--clear-http-cache`:
|
||||
|
||||
```
|
||||
$ pytest --clear-http-cache
|
||||
...
|
||||
-------------------- SmartRouter HTTP cache --------------------
|
||||
Requests found in the cache:
|
||||
https://raw.githubusercontent.com/pyscript/pyscript/main/README.md
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/repodata.json
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.asm.js
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/micropip-0.1-py3-none-any.whl
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.asm.data
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.asm.wasm
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide_py.tar
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyparsing-3.0.9-py3-none-any.whl
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/distutils.tar
|
||||
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/packaging-21.3-py3-none-any.whl
|
||||
Cache cleared
|
||||
```
|
||||
|
||||
**NOTE**: this works only if you are inside `tests/integration`, or if you
|
||||
explicitly specify `tests/integration` from the command line. This is due to
|
||||
how `pytest` decides to search for and load the various `conftest.py`.
|
||||
|
||||
@@ -1,80 +1,240 @@
|
||||
# Setting up your development environment
|
||||
|
||||
* Fork the repository - [quicklink](https://github.com/pyscript/pyscript/fork)
|
||||
These steps will help you set up your development environment. We suggest completing each step before going to the next step, as some parts have dependencies on previous commands.
|
||||
|
||||
* Clone your fork of the project
|
||||
## Prepare your repository
|
||||
|
||||
```
|
||||
* Create a fork of the [PyScript github repository](https://github.com/pyscript/pyscript/fork) to your github.
|
||||
|
||||
* In your development machine, clone your fork of PyScript. Use this command in your terminal.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/<your username>/pyscript
|
||||
```
|
||||
|
||||
* Add the original project as your upstream (this will allow you to pull the latest changes)
|
||||
* With the following command, add the original project as your upstream. This will allow you to pull the latest changes.
|
||||
|
||||
```sh
|
||||
git remote add upstream https://github.com/pyscript/pyscript.git
|
||||
git pull upstream main
|
||||
```
|
||||
|
||||
* If the above fails, try this alternative:
|
||||
|
||||
```sh
|
||||
git remote remove upstream
|
||||
git remote add upstream git@github.com:pyscript/pyscript.git
|
||||
git pull upstream main
|
||||
```
|
||||
|
||||
* cd into the `pyscriptjs` folder using the line below in your terminal (if your terminal is already in pyscript then use **cd pyscriptjs** instead)
|
||||
## Install the dependencies
|
||||
|
||||
```
|
||||
* change directory into `pyscriptjs` using this command:
|
||||
|
||||
```sh
|
||||
cd pyscript/pyscriptjs
|
||||
```
|
||||
|
||||
* Install the dependencies with the command below (you must have `nodejs` >=16 and `make`)
|
||||
We need to ensure that we have installed `conda`, `nodejs` >= 16 and `make`, before we can continue.
|
||||
|
||||
* Install `conda` by downloading one of the following packages that include it [MiniConda](https://docs.conda.io/en/latest/miniconda.html) or [Anaconda](https://www.anaconda.com/download/).
|
||||
|
||||
* Install `nodejs` with at least version 16. This can be downloaded at [https://nodejs.org](https://nodejs.org)
|
||||
|
||||
* Ensure that `make` is available on your system:
|
||||
|
||||
* *Linux*. `make` is usually installed by default in most Linux distributions. In the case it is not installed, run the terminal command `sudo apt install make`.
|
||||
|
||||
* *OS X*. Install Apple Developer Tools. `make` is included in this package.
|
||||
|
||||
* *Windows*. It is recommended to use either Windows Subsystem for Linux (WSL) or GNUWin32 for installing `make`. Instructions can be found [in this StackOverflow question](https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows).
|
||||
|
||||
* The following command will download and install the rest of the PyScript dependencies:
|
||||
|
||||
```
|
||||
make setup
|
||||
```
|
||||
**NOTE**: If `make setup` gives a node/npm version required error then go to [troubleshooting](https://github.com/pyscript/pyscript/blob/main/TROUBLESHOOTING.md)
|
||||
|
||||
* You can also run the examples locally by running the command below in your terminal
|
||||
* **NOTE**: If `make setup` gives an error on an incompatible version for node or npm, please refer to [troubleshooting](https://github.com/pyscript/pyscript/blob/main/TROUBLESHOOTING.md).
|
||||
|
||||
## Activating the environment
|
||||
|
||||
* After the above `make setup` command is completed, it will print out the command for activating the environment using the following format. Use this to work on the development environment:
|
||||
|
||||
```
|
||||
conda activate <environment name>
|
||||
```
|
||||
|
||||
## Deactivating the environment
|
||||
|
||||
* To deactivate the environment, use the following command:
|
||||
```
|
||||
conda deactivate
|
||||
```
|
||||
|
||||
|
||||
# Running PyScript examples server
|
||||
|
||||
The examples server is used to view and edit the example files.
|
||||
|
||||
* change directory into `pyscriptjs` using this command:
|
||||
|
||||
```sh
|
||||
cd pyscript/pyscriptjs
|
||||
```
|
||||
|
||||
* To build the examples, run this command:
|
||||
|
||||
```
|
||||
make examples
|
||||
```
|
||||
|
||||
* Run ***npm run dev*** to build and run the dev server. This will also watch for changes and rebuild when a file is saved.
|
||||
* To serve the examples, run this command:
|
||||
|
||||
```sh
|
||||
python -m http.server 8080 --directory examples
|
||||
```
|
||||
|
||||
* Alternately, you can also run this command if conda is not activated:
|
||||
|
||||
```sh
|
||||
conda run -p <environment name> python -m http.server 8080 --directory examples
|
||||
```
|
||||
|
||||
* You can access the examples server by visiting the following url in your browser: [http://localhost:8080](http://localhost:8080)
|
||||
|
||||
|
||||
# Running the PyScript development server
|
||||
|
||||
The PyScript development server will regularly check for any changes in the src directory. If any changes were detected, the server will rebuild itself to reflect the changes. This is useful for development with PyScript.
|
||||
|
||||
* change directory into `pyscriptjs` using this command:
|
||||
|
||||
```sh
|
||||
cd pyscript/pyscriptjs
|
||||
```
|
||||
|
||||
* Use the following command to build and run the PyScript dev server.
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
**NOTE**: To access your local build paste `http://localhost:8080` into your browser
|
||||
|
||||
* You can access the PyScript development server by visiting the following url in your browser: [http://localhost:8080](http://localhost:8080)
|
||||
|
||||
Now that node and npm have both been updated `make setup` should work, and you can continue [setting up your local environment](setting-up-environment.md) without problems (hopefully).
|
||||
# Setting up the test environment
|
||||
|
||||
A key to good development is to perform tests before sending a Pull Request for your changes.
|
||||
|
||||
## Setting up and building the docs
|
||||
## Install the dependencies
|
||||
|
||||
To build the documentation locally first make sure you are in the `docs` directory.
|
||||
* change directory into `pyscriptjs` using this command:
|
||||
|
||||
You'll need `make` and `conda` installed in your machine. The rest of the environment should be automatically download and created for you once you use the command:
|
||||
```sh
|
||||
cd pyscript/pyscriptjs
|
||||
```
|
||||
|
||||
* The following command will download the dependencies needed for running the tests:
|
||||
|
||||
```
|
||||
make setup
|
||||
```
|
||||
|
||||
Use `conda activate $environment_name` to activate your environment.
|
||||
* If you are not using a conda environment, or wish to install the dependencies manually, here are the packages needed:
|
||||
* `pillow`
|
||||
* `requests`
|
||||
* `numpy`
|
||||
* `playwright`
|
||||
* `pytest-playwright`. Note that this is only available as a `pip` package.
|
||||
|
||||
To add new information to the documentation make sure you conform with PyScript's code of conduct and with the general principles of Diataxis. Don't worry about reading too much on it, just do your best to keep your contributions on the correct axis.
|
||||
## Activating the environment
|
||||
|
||||
Write your documentation files using [Markedly Structured Text](https://myst-parser.readthedocs.io/en/latest/syntax/optional.html), which is very similar to vanilla Markdown but with some addons to create the documentation infrastructure.
|
||||
* After the above `make setup` command is completed, it will print out the command for activating the environment using the following format:
|
||||
|
||||
Once done, initialize a server to check your work:
|
||||
```
|
||||
conda activate <environment name>
|
||||
```
|
||||
|
||||
## Deactivating the environment
|
||||
|
||||
* To deactivate the environment, use the following command:
|
||||
```
|
||||
conda deactivate
|
||||
```
|
||||
|
||||
## Running the tests
|
||||
|
||||
* After setting up the test environment and while the environment is activated, you can run the tests with the following command:
|
||||
|
||||
```
|
||||
make test
|
||||
```
|
||||
|
||||
For more information about PyScript's testing framework, head over to the [development process](developing.md) page.
|
||||
|
||||
# Setting up your documentation environment
|
||||
|
||||
The documentation environment is separate from the development environment. It is used for updating and reviewing the documentation before deployment.
|
||||
|
||||
## Installing the dependencies
|
||||
|
||||
* change directory into the `docs` using this command:
|
||||
|
||||
```sh
|
||||
cd pyscript/docs
|
||||
```
|
||||
|
||||
* The following command will download, install the dependencies, and create the environment for you:
|
||||
|
||||
```
|
||||
make setup
|
||||
```
|
||||
|
||||
(activating-documentation-environment)=
|
||||
## Activating the environment
|
||||
|
||||
* After the above `make setup` command is completed, it will print out the command for activating the environment using the following format:
|
||||
|
||||
```
|
||||
conda activate <docs environment name>
|
||||
```
|
||||
|
||||
Note that the docs environment path is different from the developer's environment path.
|
||||
|
||||
## Deactivating the environment
|
||||
|
||||
* To deactivate the environment, use the following command:
|
||||
```
|
||||
conda deactivate
|
||||
```
|
||||
|
||||
## Contributing to the documentation
|
||||
|
||||
* Before sending a pull request, we recommend that your documentation conforms with [PyScript's code of conduct](https://github.com/pyscript/governance/blob/main/CODE-OF-CONDUCT.md) and with the general principles of [Diataxis](https://diataxis.fr/). Don't worry about reading too much on it, just do your best to keep your contributions on the correct axis.
|
||||
|
||||
* Write your documentation files using [Markedly Structured Text](https://myst-parser.readthedocs.io/en/latest/syntax/optional.html). This is similar to Markdown but with some addons to create the documentation infrastructure.
|
||||
|
||||
## Reviewing your work
|
||||
|
||||
* Before sending a Pull Request, review your work by starting the documentation server. To do this, use the following command:
|
||||
|
||||
```
|
||||
make livehtml
|
||||
```
|
||||
|
||||
Visible here: [http://127.0.0.1:8000](http://127.0.0.1:8000)
|
||||
You can visit the documentation server by opening a browser and visiting [http://127.0.0.1:8000](http://127.0.0.1:8000).
|
||||
|
||||
## Setting up and building tests
|
||||
* Alternately, you can open a static documentation server. Unlike the above, this will not automatically update any changes done after running this server. To see the changes done, you will need to manually stop and restart the server. To do this, use the following command:
|
||||
|
||||
You'll need to install the following to have a functional test environment: `playwright`, `pytest-playwright`, `pillow`, `requests` and `numpy`.
|
||||
```
|
||||
make htmlserve
|
||||
```
|
||||
|
||||
`pytest-playwright`is only available as a `pip` package so we recommend that you install `playwright` and `pytest` from `pip`.
|
||||
You can visit the documentation server by opening a browser and visiting [http://127.0.0.1:8080](http://127.0.0.1:8080).
|
||||
|
||||
If you're interested to learn more about PyScript's testing framework, head over to the [development process](developing.md) page.
|
||||
* To stop either server, press `ctrl+C` or `command+C` while the shell running the command is active.
|
||||
|
||||
* Note: If the above make commands failed, you need to activate the documentation environment first before running any of the commands. Refer to [Activating the environment](#activating-documentation-environment) section above.
|
||||
|
||||
# PyScript Demonstrator
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies:
|
||||
- sphinx-copybutton
|
||||
- sphinx-design
|
||||
- sphinx-togglebutton
|
||||
- nodejs=16
|
||||
|
||||
- pip:
|
||||
- sphinxemoji
|
||||
|
||||
179
docs/guides/event-handlers.md
Normal file
179
docs/guides/event-handlers.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Event handlers in PyScript
|
||||
|
||||
PyScript offer two ways to subscribe to Javascript event handlers:
|
||||
|
||||
## Subscribe to event with `py-*` attributes
|
||||
|
||||
The value of the attribute contains python code which will be executed when the event is fired. A very common pattern is to call a function which does further work, for example:
|
||||
|
||||
```html
|
||||
<button id="noParam" py-click="say_hello_no_param()">
|
||||
No Event - No Params py-click
|
||||
</button>
|
||||
<button id="withParam" py-click="say_hello_with_param('World')">
|
||||
No Event - With Params py-click
|
||||
</button>
|
||||
```
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
def say_hello_no_param():
|
||||
print("Hello!")
|
||||
|
||||
def say_hello_with_param(name):
|
||||
print("Hello " + name + "!")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
Note that py-\* attributes need a _function call_
|
||||
|
||||
Supported py-\* attributes can be seen in the [PyScript API reference](<[../api-reference.md](https://github.com/pyscript/pyscript/blob/66b57bf812dcc472ed6ffee075ace5ced89bbc7c/pyscriptjs/src/components/pyscript.ts#L119-L260)>).
|
||||
|
||||
## Subscribe to event with `addEventListener`
|
||||
|
||||
You can also subscribe to an event using the `addEventListener` method of the DOM element. This is useful if you want to pass event object to the event handler.
|
||||
|
||||
```html
|
||||
<button id="two">add_event_listener passes event</button>
|
||||
```
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
from js import console, document
|
||||
from pyodide.ffi.wrappers import add_event_listener
|
||||
|
||||
def hello_args(*args):
|
||||
console.log(f"Hi! I got some args! {args}")
|
||||
|
||||
add_event_listener(document.getElementById("two"), "click", hello_args)
|
||||
</py-script>
|
||||
```
|
||||
|
||||
or using the `addEventListener` method of the DOM element:
|
||||
|
||||
```html
|
||||
<button id="three">add_event_listener passes event</button>
|
||||
```
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
from js import console, document
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
def hello_args(*args):
|
||||
console.log(f"Hi! I got some args! {args}")
|
||||
|
||||
document.getElementById("three").addEventListener("click", create_proxy(hello_args))
|
||||
</py-script>
|
||||
```
|
||||
|
||||
or using the PyScript Element class:
|
||||
|
||||
```html
|
||||
<button id="four">add_event_listener passes event</button>
|
||||
```
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
from js import console
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
def hello_args(*args):
|
||||
console.log(f"Hi! I got some args! {args}")
|
||||
|
||||
Element("four").element.addEventListener("click", create_proxy(hello_args))
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## JavaScript to PyScript and From PyScript to JavaScript
|
||||
|
||||
If you're wondering about how to pass objects from JavaScript to PyScript and/or the other way around head over to the [Passing Objects](passing-objects.md) page.
|
||||
|
||||
|
||||
### Exporting all Global Python Objects
|
||||
|
||||
We can use our new `createObject` function to "export" the entire Python global object dictionary as a JavaScript object:
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
from js import createObject
|
||||
from pyodide.ffi import create_proxy
|
||||
createObject(create_proxy(globals()), "pyodideGlobals")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
This will make all Python global variables available in JavaScript with `pyodideGlobals.get('my_variable_name')`.
|
||||
|
||||
(Since PyScript tags evaluate _after_ all JavaScript on the page, we can't just dump a `console.log(...)` into a `<script>` tag, since that tag will evaluate before any PyScript has a chance to. We need to delay accessing the Python variable in JavaScript until after the Python code has a chance to run. The following example uses a button with `id="do-math"` to achieve this, but any method would be valid.)
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
# create some Python objects:
|
||||
symbols = {'pi': 3.1415926, 'e': 2.7182818}
|
||||
|
||||
def rough_exponential(x):
|
||||
return symbols['e']**x
|
||||
|
||||
class Circle():
|
||||
def __init__(self, radius):
|
||||
self.radius = radius
|
||||
|
||||
@property
|
||||
def area:
|
||||
return symbols['pi'] * self.radius**2
|
||||
</py-script>
|
||||
```
|
||||
|
||||
```html
|
||||
<input type="button" value="Log Python Variables" id="do-mmath" />
|
||||
<script>
|
||||
document.getElementById("do-math").addEventListener("click", () => {
|
||||
const exp = pyodideGlobals.get("rough_exponential");
|
||||
console.log("e squared is about ${exp(2)}");
|
||||
const c = pyodideGlobals.get("Circle")(4);
|
||||
console.log("The area of c is ${c.area}");
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Exporting Individual Python Objects
|
||||
|
||||
We can also export individual Python objects to the JavaScript global scope if we wish.
|
||||
|
||||
(As above, the following example uses a button to delay the execution of the `<script>` until after the PyScript has run.)
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
import js
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
# Create 3 python objects
|
||||
language = "Python 3"
|
||||
animals = ['dog', 'cat', 'bird']
|
||||
multiply3 = lambda a, b, c: a * b * c
|
||||
|
||||
# js object can be named the same as Python objects...
|
||||
js.createObject(language, "language")
|
||||
|
||||
# ...but don't have to be
|
||||
js.createObject(create_proxy(animals), "animals_from_py")
|
||||
|
||||
# functions are objects too, in both Python and Javascript
|
||||
js.createObject(create_proxy(multiply3), "multiply")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
```html
|
||||
<input type="button" value="Log Python Variables" id="log-python-variables" />
|
||||
<script>
|
||||
document
|
||||
.getElementById("log-python-variables")
|
||||
.addEventListener("click", () => {
|
||||
console.log(`Nice job using ${language}`);
|
||||
for (const animal of animals_from_py) {
|
||||
console.log(`Do you like ${animal}s? `);
|
||||
}
|
||||
console.log(`2 times 3 times 4 is ${multiply(2, 3, 4)}`);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
@@ -35,7 +35,8 @@ for dealing with the response, such as `json()` or `status`. See the
|
||||
[FetchResponse documentation](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.FetchResponse)
|
||||
for more information.
|
||||
|
||||
# Example
|
||||
## Example
|
||||
|
||||
We will make async HTTP requests to [JSONPlaceholder](https://jsonplaceholder.typicode.com/)'s fake API using `pyfetch`.
|
||||
First we write a helper function in pure Python that makes a request and returns the response. This function
|
||||
makes it easier to make specific types of requests with the most common parameters.
|
||||
@@ -70,6 +71,7 @@ async def request(url: str, method: str = "GET", body: Optional[str] = None,
|
||||
response = await pyfetch(url, **kwargs)
|
||||
return response
|
||||
```
|
||||
|
||||
This function is a wrapper for `pyfetch`, which is a wrapper for the `fetch` API. It is a coroutine function,
|
||||
so it must be awaited. It also has type hints, which are not required, but are useful for IDEs and other tools.
|
||||
The basic idea is that the `PyScript` will import and call this function, then await the response. Therefore,
|
||||
@@ -160,7 +162,8 @@ concluding html code.
|
||||
The very first thing to notice is the `py-config` tag. This tag is used to import Python files into the `PyScript`.
|
||||
In this case, we are importing the `request.py` file, which contains the `request` function we wrote above.
|
||||
|
||||
### `py-script` tag for making async HTTP requests.
|
||||
### `py-script` tag for making async HTTP requests
|
||||
|
||||
Next, the `py-script` tag contains the actual Python code where we import `asyncio` and `json`,
|
||||
which are required or helpful for the `request` function.
|
||||
The `# GET`, `# POST`, `# PUT`, `# DELETE` blocks show examples of how to use the `request` function to make basic
|
||||
@@ -169,6 +172,7 @@ HTTP requests. The `await` keyword is required not only for the `request` functi
|
||||
faster ones.
|
||||
|
||||
### HTTP Requests
|
||||
|
||||
HTTP requests are a very common way to communicate with a server. They are used for everything from getting data from
|
||||
a database, to sending emails, to authorization, and more. Due to safety concerns, files loaded from the
|
||||
local file system are not accessible by `PyScript`. Therefore, the proper way to load data into `PyScript` is also
|
||||
@@ -182,31 +186,38 @@ function or to `pyfetch`. See the
|
||||
HTTP requests are defined by standards-setting bodies in [RFC 1945](https://www.rfc-editor.org/info/rfc1945) and
|
||||
[RFC 9110](https://www.rfc-editor.org/info/rfc9110).
|
||||
|
||||
# Conclusion
|
||||
## Conclusion
|
||||
|
||||
This tutorial demonstrates how to make HTTP requests using `pyfetch` and the `FetchResponse` objects. Importing Python
|
||||
code/files into the `PyScript` using the `py-config` tag is also covered.
|
||||
|
||||
Although a simple example, the principals here can be used to create complex web applications inside of `PyScript`,
|
||||
or load data into `PyScript` for use by an application, all served as a static HTML page, which is pretty amazing!
|
||||
|
||||
## API Quick Reference
|
||||
|
||||
# API Quick Reference
|
||||
## pyodide.http.pyfetch
|
||||
### Usage
|
||||
|
||||
### pyfetch Usage
|
||||
|
||||
```python
|
||||
await pyodide.http.pyfetch(url: str, **kwargs: Any) -> FetchResponse
|
||||
```
|
||||
|
||||
Use `pyfetch` to make HTTP requests in `PyScript`. This is a wrapper around the `fetch` API. Returns a `FetchResponse`.
|
||||
|
||||
- [`pyfetch` Docs.](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.pyfetch)
|
||||
|
||||
## pyodide.http.FetchResponse
|
||||
### Usage
|
||||
|
||||
### FetchResponse Usage
|
||||
|
||||
```python
|
||||
response: pyodide.http.FetchResponse = await <pyfetch call>
|
||||
status = response.status
|
||||
json = await response.json()
|
||||
```
|
||||
|
||||
Class for handling HTTP responses. This is a wrapper around the `JavaScript` fetch `Response`. Contains common (async)
|
||||
methods and properties for handling HTTP responses, such as `json()`, `url`, `status`, `headers`, etc.
|
||||
|
||||
|
||||
@@ -18,4 +18,5 @@ passing-objects
|
||||
http-requests
|
||||
asyncio
|
||||
custom-plugins
|
||||
event-handlers
|
||||
```
|
||||
|
||||
@@ -2,11 +2,24 @@
|
||||
|
||||
Welcome to the PyScript documentation!
|
||||
|
||||
PyScript provides a way for you to run Python code directly in your browser, giving
|
||||
anyone the ability to program without infrastructure barriers. Add an interactive
|
||||
Python REPL directly to your website, share an interactive dashboard with a colleague
|
||||
as an HTML file, or create a client-side Python-powered web application. This documentation
|
||||
will show you how.
|
||||
PyScript is a programming platform that allows you to create web applications that run in the browser, using Python.
|
||||
That creates some really interesting benefits:
|
||||
|
||||
* Using Python directly in the browser allows to create applications with an easier and more user friendly language
|
||||
* Scalability: since applications run directly in the browser and not on a server somewhere, servers don't need to
|
||||
scale as much if the number of users of an application grows exponentially
|
||||
* Shareability: applications can be shared as easily as sharing an URL. Can't get easier than that ;)
|
||||
* Multi-Platform support: since the browser is the underlying system where PyScript applications run, applications
|
||||
can run anywhere a modern browser is installed, on windows, linux, mac, mobile, or even a Tesla! :)
|
||||
* Security: since PyScript runs core in the Browser (via Web Assembly) in a sandbox fashion and the browsers offers
|
||||
a very strict level of security, code never have access files or part of the underlying system without user permission,
|
||||
making it a great option in terms of security.
|
||||
* User Friendly APIs: web APIs are very vast and, sometimes, complicated. PyScript offers smaller and more user friendly
|
||||
APIs for the most common use cases while also providing an option to access the full Web APIs as well.
|
||||
|
||||
We hope you'll enjoy the project and create so many incredible things with it! To learn more, consult our documentation.
|
||||
|
||||
|
||||
|
||||
::::{grid} 2
|
||||
:gutter: 3
|
||||
|
||||
@@ -5,15 +5,13 @@ PyScript provides a convenient syntax for mapping JavaScript events to PyScript
|
||||
For example, you can use the following code to connect the click event to a button:
|
||||
|
||||
```
|
||||
<button id="py-click" py-onClick="foo()">Click me</button>
|
||||
<button id="py-click" py-click="foo()">Click me</button>
|
||||
```
|
||||
|
||||
Here is a list of all the available event mappings:
|
||||
|
||||
| PyScript Event Name | DOM Event Name |
|
||||
|-------------------|----------------|
|
||||
| py-onClick | click |
|
||||
| py-onKeyDown | keydown |
|
||||
| py-afterprint | afterprint |
|
||||
| py-beforeprint | beforeprint |
|
||||
| py-beforeunload | beforeunload |
|
||||
|
||||
@@ -35,7 +35,7 @@ Display will throw an exception if the target is not clear. E.g. the following c
|
||||
# from event handlers
|
||||
display('hello')
|
||||
</py-script>
|
||||
<button id="my-button" py-onClick="display_hello()">Click me</button>
|
||||
<button id="my-button" py-click="display_hello()">Click me</button>
|
||||
```
|
||||
|
||||
Because it's considered unclear if the `hello` string should be displayed underneath the `<py-script>` tag or the `<button>` tag.
|
||||
@@ -45,12 +45,11 @@ To write compliant code, make sure to specify the target using the `target` para
|
||||
```html
|
||||
<py-script>
|
||||
def display_hello():
|
||||
# this fails because we don't have any implicit target
|
||||
# from event handlers
|
||||
# this works because we give an explicit target
|
||||
display('hello', target="helloDiv")
|
||||
</py-script>
|
||||
<div id="helloDiv"></div>
|
||||
<button id="my-button" py-onClick="display_hello()">Click me</button>
|
||||
<button id="my-button" py-click="display_hello()">Click me</button>
|
||||
```
|
||||
|
||||
#### Using matplotlib with display
|
||||
|
||||
51
docs/reference/API/when.md
Normal file
51
docs/reference/API/when.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# `@when`
|
||||
|
||||
`@when(event_type:str = None, selector:str = None)`
|
||||
|
||||
The `@when` decorator attaches the decorated function or Callable as an event handler for selected objects on the page. That is, when the named event is emitted by the selected DOM elements, the decorated Python function will be called.
|
||||
|
||||
If the decorated function takes a single (non-self) argument, it will be passed the [Event object](https://developer.mozilla.org/en-US/docs/Web/API/Event) corresponding to the triggered event. If the function takes no (non-self) argument, it will be called with no arguments.
|
||||
|
||||
## Parameters
|
||||
|
||||
`event_type` - A string representing the event type to match against. This can be any of the [https://developer.mozilla.org/en-US/docs/Web/Events#event_listing](https://developer.mozilla.org/en-US/docs/Web/Events) that HTML elements may emit, as appropriate to their element type.
|
||||
|
||||
`selector` = A string containing one or more [CSS selectors](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors). The selected DOM elements will have the decorated function attacehed as an event handler.
|
||||
|
||||
## Examples:
|
||||
|
||||
The following example prints "Hello, world!" whenever the button is clicked. It demonstrates using the `@when` decorator on a Callable which takes no arguments:
|
||||
|
||||
```html
|
||||
<button id="my_btn">Click Me to Say Hi</button>
|
||||
<py-script>
|
||||
from pyscript import when
|
||||
@when("click", selector="#my_btn")
|
||||
def say_hello():
|
||||
print(f"Hello, world!")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
The following example includes three buttons - when any of the buttons is clicked, that button turns green, and the remaining two buttons turn red. This demonstrates using the `@when` decorator on a Callable which takes one argument, which is then passed the Event object from the associated event. When combined with the ability to look at other elements in on the page, this is quite a powerful feature.
|
||||
|
||||
```html
|
||||
<div id="container">
|
||||
<button>First</button>
|
||||
<button>Second</button>
|
||||
<button>Third</button>
|
||||
</div>
|
||||
<py-script>
|
||||
from pyscript import when
|
||||
import js
|
||||
|
||||
@when("click", selector="#container button")
|
||||
def highlight(evt):
|
||||
#Set the clicked button's background to green
|
||||
evt.target.style.backgroundColor = 'green'
|
||||
|
||||
#Set the background of all buttons to red
|
||||
other_buttons = (button for button in js.document.querySelectorAll('button') if button != evt.target)
|
||||
for button in other_buttons:
|
||||
button.style.backgroundColor = 'red'
|
||||
</py-script>
|
||||
```
|
||||
@@ -8,10 +8,10 @@ The `<py-config>` element should be placed within the `<body>` element.
|
||||
|
||||
## Attributes
|
||||
|
||||
| attribute | type | default | description |
|
||||
|-----------|--------|---------|---------------------------------------------------------------------------------------------------------|
|
||||
| **type** | string | "toml" | Syntax type of the `<py-config>`. Value can be `json` or `toml`. Default: "toml" if type is unspecifed. |
|
||||
| **src** | url | | Source url to an external configuration file. |
|
||||
| attribute | type | default | description |
|
||||
|-----------|--------|---------|----------------------------------------------------------------------------------------------------------|
|
||||
| **type** | string | "toml" | Syntax type of the `<py-config>`. Value can be `json` or `toml`. Default: "toml" if type is unspecified. |
|
||||
| **src** | url | | Source url to an external configuration file. |
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -4,27 +4,59 @@ The `<py-repl>` element provides a REPL(Read Eval Print Loop) to evaluate multi-
|
||||
|
||||
## Attributes
|
||||
|
||||
| attribute | type | default | description |
|
||||
|-------------------|---------|---------|---------------------------------------|
|
||||
| attribute | type | default | description |
|
||||
|-------------------|---------|---------|--------------------------------------|
|
||||
| **auto-generate** | boolean | | Auto-generates REPL after evaluation |
|
||||
| **output** | string | | The element to write output into |
|
||||
| **output-mode** | string | "" | Determines whether the output element is cleared prior to writing output |
|
||||
| **output** | string | | The id of the element to write `stdout` and `stderr` to |
|
||||
| **stderr** | string | | The id of the element to write `stderr` to |
|
||||
| **src** | string | | Resource to be preloaded into the REPL |
|
||||
|
||||
### Examples
|
||||
|
||||
#### `<py-repl>` element set to auto-generate
|
||||
### `auto-generate`
|
||||
If a \<py-repl\> tag has the `auto-generate` attribute, upon execution, another \<pr-repl\> tag will be created and added to the DOM as a sibling of the current tag.
|
||||
|
||||
### `output-mode`
|
||||
By default, the element which displays the output from a REPL is cleared (`innerHTML` set to "") prior to each new execution of the REPL. If `output-mode` == "append", that element is not cleared, and the output is appended instead.
|
||||
|
||||
### `output`
|
||||
The ID of an element in the DOM that `stdout` (e.g. `print()`), `stderr`, and the results of executing the repl are written to. Defaults to an automatically-generated \<div\> as the next sibling of the REPL itself.
|
||||
|
||||
### `stderr`
|
||||
The ID of an element in the DOM that `stderr` will be written to. Defaults to None, though writes to `stderr` will still appear in the location specified by `output`.
|
||||
|
||||
### `src`
|
||||
If a \<py-repl\> tag has the `src` attribute, during page initialization, resource in the `src` will be preloaded into the REPL. Please note that this will not run in advance. If there is content in the \<py-repl\> tag, it will be cleared and replaced with preloaded resource.
|
||||
|
||||
## Examples
|
||||
|
||||
### `<py-repl>` element set to auto-generate
|
||||
|
||||
```html
|
||||
<py-repl auto-generate="true"> </py-repl>
|
||||
```
|
||||
|
||||
#### `<py-repl>` element with output
|
||||
### `<py-repl>` element with output
|
||||
|
||||
The following will write "Hello! World!" to the div with id `replOutput`.
|
||||
|
||||
```html
|
||||
<div id="replOutput"></div>
|
||||
<py-repl output="replOutput">
|
||||
hello = "Hello world!"
|
||||
print("Hello!")
|
||||
hello = "World!"
|
||||
hello
|
||||
</py-repl>
|
||||
```
|
||||
|
||||
Note that if we `print` any element in the repl, the output will be printed in the [`py-terminal`](../plugins/py-terminal.md) if is enabled.
|
||||
Note that if we `print` from the REPL (or otherwise write to `sys.stdout`), the output will be printed in the [`py-terminal`](../plugins/py-terminal.md) if is enabled.
|
||||
|
||||
### `<py-repl>` element with src
|
||||
Preload resource from src into the REPL
|
||||
```html
|
||||
<py-repl id="py-repl" output="replOutput" src="./src/py/py_code.py">
|
||||
If a py-repl tag has the src attribute,
|
||||
the content here will be cleared and replaced.
|
||||
</py-repl>
|
||||
<div id="replOutput"></div>
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# <py-script>
|
||||
|
||||
The `<py-script>` element lets you execute multi-line Python scripts both inline and via a src attribute.
|
||||
The `<py-script>` element, also available as `<script type="py-script">`, lets you execute multi-line Python scripts both inline and via a src attribute.
|
||||
|
||||
## Attributes
|
||||
|
||||
@@ -12,13 +12,13 @@ The `<py-script>` element lets you execute multi-line Python scripts both inline
|
||||
|
||||
### output
|
||||
|
||||
If the `output` attribute is provided, any output to [sys.stdout](https://docs.python.org/3/library/sys.html#sys.stdout) or [sys.stderr](https://docs.python.org/3/library/sys.html#sys.stderr) is written to the DOM element with the ID matching the attribute. If no DOM element is found with a matching ID, a warning is shown. The msg is output to the `innerHTML` of the HTML Element, with newlines (`\n'`) converted to breaks (`<br\>`).
|
||||
If the `output` attribute is provided, any output to [sys.stdout](https://docs.python.org/3/library/sys.html#sys.stdout) or [sys.stderr](https://docs.python.org/3/library/sys.html#sys.stderr) is written to the DOM element with the ID matching the attribute. If no DOM element is found with a matching ID, a warning is shown. The msg is output to the `innerHTML` of the HTML Element, with newlines (`\n'`) converted to breaks (`<br/>`).
|
||||
|
||||
This output is in addition to the output being written to the developer console and the `<py-terminal>` if it is being used.
|
||||
|
||||
### stderr
|
||||
|
||||
If the `stderr` attribute is provided, any output to [sys.stderr](https://docs.python.org/3/library/sys.html#sys.stderr) is written to the DOM element with the ID matching the attribute. If no DOM element is found with a matching ID, a warning is shown. The msg is output to the `innerHTML` of the HTML Element, with newlines (`\n'`) converted to breaks (`<br\>`).
|
||||
If the `stderr` attribute is provided, any output to [sys.stderr](https://docs.python.org/3/library/sys.html#sys.stderr) is written to the DOM element with the ID matching the attribute. If no DOM element is found with a matching ID, a warning is shown. The msg is output to the `innerHTML` of the HTML Element, with newlines (`\n'`) converted to breaks (`<br/>`).
|
||||
|
||||
This output is in addition to the output being written to the developer console and the `<py-terminal>` if it is being used.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# pyscript
|
||||
|
||||
The code underlying PyScript is a TypeScript/JavaScript module, which is loaded and executed by the browser. This is what loads when you include, for example, `<script defer src="https://pyscript.net/latest/pyscript.js">` in your HTML.
|
||||
The code underlying PyScript is a JavaScript module, which is loaded and executed by the browser. This is what loads when you include, for example, `<script defer src="https://pyscript.net/latest/pyscript.js">` in your HTML.
|
||||
|
||||
The module is exported to the browser as `pyscript`. The exports from this module are:
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ This is one of the core plugins in PyScript, which is active by default. With it
|
||||
|
||||
## Configuration
|
||||
|
||||
You can control how `<py-terminal>` behaves by setting the value of the `terminal` configuration in your `<py-config>`.
|
||||
You can control how `<py-terminal>` behaves by setting the values of the `terminal`, `docked`, and `xterm` fields in your configuration in your `<py-config>`.
|
||||
|
||||
For the **terminal** field, these are the values:
|
||||
|
||||
| value | description |
|
||||
|-------|-------------|
|
||||
@@ -12,11 +14,50 @@ You can control how `<py-terminal>` behaves by setting the value of the `termin
|
||||
| `true` | Automatically add a `<py-terminal>` to the page |
|
||||
| `"auto"` | This is the default. Automatically add a `<py-terminal auto>`, to the page. The terminal is initially hidden and automatically shown as soon as something writes to `stdout` and/or `stderr` |
|
||||
|
||||
For the **docked** field, these are the values:
|
||||
|
||||
| value | description |
|
||||
|-------|-------------|
|
||||
| `false` | Don't dock `<py-terminal>` to the page |
|
||||
| `true` | Automatically dock a `<py-terminal>` to the page |
|
||||
| `"docked"` | This is the default. Automatically add a `<py-terminal docked>`, to the page. The terminal, once visible, is automatically shown at the bottom of the page, covering the width of such page |
|
||||
|
||||
Please note that **docked** mode is currently used as default only when `terminal="auto"`, or *terminal* default, is used.
|
||||
|
||||
In all other cases it's up to the user decide if a terminal should be docked or not.
|
||||
|
||||
For the **xterm** field, these are the values:
|
||||
|
||||
| value | description |
|
||||
|-------|-------------|
|
||||
| `false` | This is the default. The `<py-terminal>` is a simple `<pre>` tag with some CSS styling. |
|
||||
| `true` or `xterm` | The [xtermjs](http://xtermjs.org/) library is loaded and its Terminal object is used as the `<py-terminal>`. It's visibility and position are determined by the `docked` and `auto` keys in the same way as the default `<py-terminal>` |
|
||||
|
||||
The xterm.js [Terminal object](http://xtermjs.org/docs/api/terminal/classes/terminal/) can be accessed directly if you want to adjust its properties, add [custom parser hooks](http://xtermjs.org/docs/guides/hooks/), introduce [xterm.js addons](http://xtermjs.org/docs/guides/using-addons/), etc. Access is best achieved by awaiting the `xtermReady` attribute of the `<py-terminal>` HTML element itself:
|
||||
|
||||
```python
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def adjust_term_size(columns, rows):
|
||||
xterm = await js.document.querySelector('py-terminal').xtermReady
|
||||
xterm.resize(columns, rows)
|
||||
|
||||
asyncio.ensure_future(adjust_term_size(40,10))
|
||||
```
|
||||
|
||||
Some terminal-formatting packages read from specific environment variables to determine whether they should emit formatted output; PyScript does not set these variables explicitly - you may need to set them yourself, or force your terminal-formatting package into a state where it outputs correctly formatted output.
|
||||
|
||||
A couple of specific examples:
|
||||
- the [rich](https://github.com/Textualize/rich) will not, by default, output colorful text, but passing `256` or `truecolor` as an argument as the `color_system` parameter to the [Console constructor](https://rich.readthedocs.io/en/stable/reference/console.html#rich.console.Console) will force it to do so. (As of rich v13)
|
||||
- [termcolor](https://github.com/termcolor/termcolor) will not, by default, output colorful text, but setting `os.environ["FORCE_COLOR"] = "True"` or by passing `force_color=True` to the `colored()` function will force it to do so. (As of termcolor v2.3)
|
||||
|
||||
### Examples
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
terminal = true
|
||||
docked = false
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
|
||||
@@ -1,50 +1,93 @@
|
||||
# Getting started with PyScript
|
||||
|
||||
This page will guide you through getting started with PyScript.
|
||||
To start developing a PyScript, like with most applications development, you need a **development environment** where you
|
||||
write your code, a way to install the programming libraries and dependencies your code needs, and way to build and distribute
|
||||
your application.
|
||||
|
||||
Luckily, PyScript makes many of these steps much easier.
|
||||
|
||||
## Development setup
|
||||
|
||||
PyScript does not require any development environment other
|
||||
then a web browser (we recommend using [Chrome](https://www.google.com/chrome/)) and a text editor, even though using your [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) of choice might be convenient.
|
||||
PyScript does not require any specific development environment other
|
||||
than a web browser (we recommend using [Chrome](https://www.google.com/chrome/)) and a text editor, even though using your [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) of choice might be convenient.
|
||||
|
||||
If you're using [VSCode](https://code.visualstudio.com/), the
|
||||
[Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
|
||||
can be used to reload the page as you edit the HTML file.
|
||||
|
||||
**NOTE:** The easier way to get a development setup for PyScript is to use [pyscript.com](pyscript.com). It is a free service that allows
|
||||
users to create new projects from pre-created templates that already have all the project structure created and allows users
|
||||
to edit their apps, preview it and deploy with just a link, all in the same place.
|
||||
|
||||
## Installation
|
||||
|
||||
There is no installation required. In this document, we'll use
|
||||
the PyScript assets served on [https://pyscript.net](https://pyscript.net).
|
||||
There is no PyScript specific installation required in your system to start using PyScript in your browser. All you need to do is to
|
||||
simply add a reference in your application code to where your application should get PyScript from.
|
||||
|
||||
If you want to download the source and build it yourself, follow
|
||||
the instructions in the [README.md](https://github.com/pyscript/pyscript/blob/main/README.md) file.
|
||||
If you are not an experienced developer and it all sounds very complicated, don't worry, we'll get you through it in the following steps.
|
||||
|
||||
## Your first PyScript HTML file
|
||||
## Writing your first PyScript application
|
||||
|
||||
Here's a "Hello, world!" example using PyScript.
|
||||
As we hinted earlier, writing a PyScript application means writing a web application that can run code writted in Python (and other languages)
|
||||
on the web. This means that the way we create PyScript applications starts in a very similar way to how we write web applications: from an
|
||||
HTML file.
|
||||
|
||||
Using your favorite editor, create a new file called `hello.html` in
|
||||
the same directory as your PyScript, JavaScript, and CSS files with the
|
||||
following content, and open the file in your web browser. You can typically
|
||||
open an HTML by double-clicking it in your file explorer.
|
||||
To demonstrate the above, let's start from the most popular "first application example": let's write a "Hello, world!"
|
||||
example using PyScript.
|
||||
|
||||
Using your favorite editor, create a new file called `hello.html` and paste in the following content and open it in your web browser. (You can typically
|
||||
open an HTML by double-clicking it in your file explorer.):
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My First PyScript APP: Hello World!</title>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-script>
|
||||
from pyscript import display
|
||||
print('Hello, World!')
|
||||
display('Hello, World!')
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
<a href="https://fpliger.pyscriptapps.com/hello-world-minimal-example/latest/" target="_blank">or open this example on pyscript.com</a>
|
||||
|
||||
You should see "Hello World!" printed in your page and in your Javascript Console (don't worry if
|
||||
you don't know what it means yet, we'll get into that later).
|
||||
|
||||
## Serving your application
|
||||
|
||||
Now what we have written our first application, it's important talk about how we can access it.
|
||||
|
||||
In the example above, we were able to visualize it by simply opening the local file from our
|
||||
system directly with the browser. While that's a very simple and fast way to open our application,
|
||||
it is not very recommended because browsers will forbid many features when accessing files this way,
|
||||
for security reasons. When this is the case, you may see your Python code in the text of the webpage,
|
||||
and the [browser developer console](https://balsamiq.com/support/faqs/browserconsole/) may show an
|
||||
error like *"Cross origin requests are only supported for HTTP."*
|
||||
|
||||
In short, when browsers visualize a web page, they expect them to be served by a
|
||||
web server.
|
||||
|
||||
For the rest of this documentation, we'll be presenting examples and snippets and host them on
|
||||
pyscript.com. Users can reference the [serving your application](serving-your-application.md) at
|
||||
anytime for other options.
|
||||
|
||||
## A more complex example
|
||||
|
||||
Now that we know how you can create a simple 'Hello, World!' example, let's see a more complex example. This example will use the Demo created by [Cheuk Ting Ho](https://github.com/Cheukting). In this example, we will use more features from PyScript.
|
||||
Now that we know how you can create a simple 'Hello, World!' example, let's see a more complex example.
|
||||
This example will use the Demo created by [Cheuk Ting Ho](https://github.com/Cheukting). In this example, we will use more features from PyScript.
|
||||
|
||||
### Setting up the base index file
|
||||
|
||||
@@ -249,8 +292,8 @@ Now that we have a way to explore the data using `py-repl` and a way to create t
|
||||
<label for="all"> All 🍧</label>
|
||||
<input type="radio" id="chocolate" name="flavour" value="COCOA">
|
||||
<label for="chocolate"> Chocolate 🍫</label>
|
||||
<input type="radio" id="cherrie" name="flavour" value="CHERRIES">
|
||||
<label for="cherrie"> Cherries 🍒</label>
|
||||
<input type="radio" id="cherry" name="flavour" value="CHERRIES">
|
||||
<label for="cherry"> Cherries 🍒</label>
|
||||
<input type="radio" id="berries" name="flavour" value="BERRY">
|
||||
<label for="berries"> Berries 🍓</label>
|
||||
<input type="radio" id="cheese" name="flavour" value="CHEESE">
|
||||
|
||||
@@ -35,5 +35,4 @@ glob:
|
||||
---
|
||||
py-config-fetch
|
||||
py-config-interpreter
|
||||
writing-to-page
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Handling click events
|
||||
|
||||
This tutorial will show you how to use the `py-click` attribute to handle mouse clicks on elements on your page. The `py-click` attribute is a special attribute that allows you to specify a Python function that will be called when the element is clicked.
|
||||
This tutorial will show you how to use the `py-click` attribute to handle mouse clicks on elements on your page. The `py-click` attribute is a special attribute that allows you to specify a Python function that will be called when the element is clicked. There are many other events such as py-mouseover, py-focus, py-input, py-keyress etc, which can be used as well. They are listed here [Attr-to-Event](../reference/API/attr_to_event.html)
|
||||
|
||||
## Development setup
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ To get started, let's create a new `index.html` file and import `pyscript.js`.
|
||||
</html>
|
||||
```
|
||||
|
||||
We are using the pyodide CDN to setup our interpreter, but you can also download the files from [the pyodide GitHub release](https://github.com/pyodide/pyodide/releases/tag/0.22.0a3), unzip them and use the `pyodide.js` file as your interpreter.
|
||||
We are using the pyodide CDN to setup our interpreter, but you can also download the files from [the pyodide GitHub releases](https://github.com/pyodide/pyodide/releases/), unzip them and use the `pyodide.js` file as your interpreter.
|
||||
|
||||
## Setting the interpreter
|
||||
|
||||
@@ -47,8 +47,8 @@ To set the interpreter, you can use the `interpreter` configuration in the `py-c
|
||||
<body>
|
||||
<py-config>
|
||||
[[interpreters]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js"
|
||||
name = "pyodide-0.22.0a3"
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.23.0/full/pyodide.js"
|
||||
name = "pyodide-0.23.0"
|
||||
lang = "python"
|
||||
</py-config>
|
||||
</body>
|
||||
@@ -75,8 +75,8 @@ To confirm that the interpreter is set correctly, you can open the DevTools and
|
||||
<body>
|
||||
<py-config>
|
||||
[[interpreters]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js"
|
||||
name = "pyodide-0.22.0a3"
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.23.0/full/pyodide.js"
|
||||
name = "pyodide-0.23.0"
|
||||
lang = "python"
|
||||
</py-config>
|
||||
<py-script>
|
||||
|
||||
@@ -72,10 +72,10 @@ Now that we have installed the dependencies, we need to patch the Requests libra
|
||||
|
||||
<py-script>
|
||||
import pyodide_http
|
||||
pyodide_http.patch()
|
||||
pyodide_http.patch_all()
|
||||
</py-script>
|
||||
</body>
|
||||
</html
|
||||
</html>
|
||||
```
|
||||
|
||||
## Making a request
|
||||
@@ -104,7 +104,7 @@ Finally, let's make a request to the JSON Placeholder API to confirm that everyt
|
||||
import pyodide_http
|
||||
|
||||
# Patch the Requests library so it works with Pyscript
|
||||
pyodide_http.patch()
|
||||
pyodide_http.patch_all()
|
||||
|
||||
# Make a request to the JSON Placeholder API
|
||||
response = requests.get("https://jsonplaceholder.typicode.com/todos")
|
||||
|
||||
@@ -8,7 +8,15 @@
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<style>
|
||||
py-script {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000">
|
||||
@@ -29,10 +37,11 @@
|
||||
"vega_datasets"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
from pyscript import display
|
||||
import altair as alt
|
||||
from vega_datasets import data
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<section class="pyscript">
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
[[fetch]]
|
||||
files = ["./antigravity.py"]
|
||||
|
||||
@@ -32,8 +32,16 @@
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<style>
|
||||
py-script {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000">
|
||||
@@ -57,7 +65,7 @@
|
||||
"xyzservices"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"numpy",
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<py-tutor modules="d3.py">
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
[[fetch]]
|
||||
files = ["./d3.py"]
|
||||
|
||||
@@ -7,8 +7,16 @@
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<style>
|
||||
py-script {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000">
|
||||
@@ -29,11 +37,12 @@
|
||||
"pandas"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
from pyscript import display
|
||||
import folium
|
||||
import json
|
||||
import pandas as pd
|
||||
|
||||
@@ -11,16 +11,19 @@
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<script defer src="../../pyscriptjs/build/pyscript.js"></script>
|
||||
<!-- <script defer src="https://pyscript.net/latest/pyscript.js"></script> -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-script>
|
||||
from js import handTrack, requestAnimationFrame
|
||||
from js import handTrack, requestAnimationFrame, console
|
||||
from pyodide import create_once_callable
|
||||
import asyncio
|
||||
|
||||
update_note = Element("update-note")
|
||||
canvas = Element("canvas")
|
||||
video = Element("myvideo")
|
||||
context = canvas.element.getContext("2d")
|
||||
|
||||
isVideo = False
|
||||
@@ -33,7 +36,7 @@
|
||||
"scoreThreshold": 0.6, # confidence threshold for predictions.
|
||||
}
|
||||
|
||||
def toggle_video(evt):
|
||||
def toggle_video():
|
||||
global isVideo
|
||||
if (not isVideo):
|
||||
update_note.write("Starting video")
|
||||
@@ -112,7 +115,7 @@
|
||||
id="trackbutton"
|
||||
class="bx--btn bx--btn--secondary"
|
||||
type="button"
|
||||
py-onClick="toggle_video()"
|
||||
py-click="toggle_video()"
|
||||
>
|
||||
Toggle Video
|
||||
</button>
|
||||
@@ -124,13 +127,11 @@
|
||||
>
|
||||
Next Image
|
||||
</button>
|
||||
<div id="update-note" py-mount class="updatenote mt10">
|
||||
loading model ..
|
||||
</div>
|
||||
<div id="update-note" class="updatenote mt10">loading model ..</div>
|
||||
</div>
|
||||
<div>
|
||||
<video autoplay="autoplay" id="myvideo" py-mount="video"></video>
|
||||
<canvas id="canvas" py-mount class="border canvasbox"></canvas>
|
||||
<canvas id="canvas" class="border canvasbox"></canvas>
|
||||
</div>
|
||||
<script src="lib/handtrack.min.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -12,7 +12,15 @@
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<style>
|
||||
py-script {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -28,7 +36,7 @@
|
||||
<py-tutor>
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
@@ -36,6 +44,7 @@
|
||||
Hello world! <br />
|
||||
This is the current date and time, as computed by Python:
|
||||
<py-script>
|
||||
from pyscript import display
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
display(now.strftime("%m/%d/%Y, %H:%M:%S"))
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
from pyodide import create_once_callable
|
||||
import asyncio
|
||||
|
||||
update_note = Element("update-note")
|
||||
canvas = Element("canvas")
|
||||
video = Element("myvideo")
|
||||
context = canvas.element.getContext("2d")
|
||||
|
||||
isVideo = False
|
||||
@@ -60,7 +63,7 @@
|
||||
"scoreThreshold": 0.6, # confidence threshold for predictions.
|
||||
}
|
||||
|
||||
def toggle_video(evt):
|
||||
def toggle_video():
|
||||
global isVideo
|
||||
player.jump()
|
||||
|
||||
@@ -140,7 +143,7 @@
|
||||
id="trackbutton"
|
||||
class="bx--btn bx--btn--secondary"
|
||||
type="button"
|
||||
py-onClick="toggle_video()"
|
||||
py-click="toggle_video()"
|
||||
>
|
||||
Start Video
|
||||
</button>
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
<title>PyMarkdown</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="../build/pyscript.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<script defer src="../build/pyscript.js"></script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -19,8 +22,8 @@
|
||||
"markdown"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_markdown.py",
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_markdown.py",
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -28,11 +31,11 @@
|
||||
"matplotlib"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
<script type="py">
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.tri as tri
|
||||
import numpy as np
|
||||
@@ -66,7 +69,7 @@
|
||||
ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
|
||||
|
||||
display(fig1, target="mpl")
|
||||
</py-script>
|
||||
</script>
|
||||
</py-tutor>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"matplotlib"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
id="run-all-button"
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
py-onClick="run_all_micrograd_demo()"
|
||||
py-click="run_all_micrograd_demo()"
|
||||
>
|
||||
Run All</button
|
||||
><br />
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
}
|
||||
</py-config>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<py-tutor>
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
packages = ["pandas"]
|
||||
</py-config>
|
||||
@@ -101,7 +101,7 @@
|
||||
df = pd.DataFrame()
|
||||
|
||||
|
||||
def loadFromURL(*ags, **kws):
|
||||
def loadFromURL(*args, **kws):
|
||||
global df
|
||||
|
||||
# clear dataframe & output
|
||||
|
||||
@@ -3,27 +3,10 @@
|
||||
<title>Panel Example</title>
|
||||
<meta charset="iso-8859-1" />
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png" />
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.14.1/dist/panel.min.js"
|
||||
></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -41,6 +24,12 @@
|
||||
<div id="simple_app"></div>
|
||||
|
||||
<py-tutor>
|
||||
<script defer src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.js"></script>
|
||||
<script defer src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js"></script>
|
||||
<script defer src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.14.1/dist/panel.min.js"></script>
|
||||
<style>py-script{display:none}</style>
|
||||
<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script>
|
||||
<py-config>
|
||||
packages = [
|
||||
"https://cdn.holoviz.org/panel/0.14.3/dist/wheels/bokeh-2.4.3-py3-none-any.whl",
|
||||
@@ -48,7 +37,7 @@
|
||||
"panel==0.14.1"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
"panel==0.13.1"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"panel==0.13.1"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
@@ -137,6 +137,7 @@
|
||||
import altair as alt
|
||||
import panel as pn
|
||||
import pandas as pd
|
||||
import param
|
||||
|
||||
from sklearn.cluster import KMeans
|
||||
from pyodide.http import open_url
|
||||
@@ -170,7 +171,7 @@
|
||||
y=alt.Y(y, scale=alt.Scale(zero=False)),
|
||||
shape='labels',
|
||||
color='species'
|
||||
).add_selection(brush).properties(width=800) +
|
||||
).add_params(brush).properties(width=800) +
|
||||
alt.Chart(centers)
|
||||
.mark_point(size=250, shape='cross', color='black')
|
||||
.encode(x=x+':Q', y=y+':Q')
|
||||
@@ -197,8 +198,8 @@
|
||||
@pn.depends(x, y, n_clusters, watch=True)
|
||||
def update_chart(*events):
|
||||
chart.object = get_chart(x.value, y.value, table.value)
|
||||
chart.selection.param.watch(update_filters, 'brush')
|
||||
|
||||
@param.depends('brush', watch=True)
|
||||
def update_filters(event=None):
|
||||
filters = []
|
||||
for k, v in (getattr(event, 'new') or {}).items():
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
"panel==0.13.1"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
|
||||
170
examples/py_list.py
Normal file
170
examples/py_list.py
Normal file
@@ -0,0 +1,170 @@
|
||||
import time
|
||||
from datetime import datetime as dt
|
||||
from textwrap import dedent
|
||||
|
||||
import js
|
||||
from pyscript import Element, Plugin, create
|
||||
|
||||
plugin = Plugin("PyList")
|
||||
|
||||
|
||||
class PyItemTemplate(Element):
|
||||
label_fields = None
|
||||
|
||||
def __init__(self, data, labels=None, state_key=None, parent=None):
|
||||
self.data = data
|
||||
|
||||
self.register_parent(parent)
|
||||
|
||||
if not labels:
|
||||
labels = list(self.data.keys())
|
||||
self.labels = labels
|
||||
|
||||
self.state_key = state_key
|
||||
|
||||
super().__init__(self._id)
|
||||
|
||||
def register_parent(self, parent):
|
||||
self._parent = parent
|
||||
if parent:
|
||||
self._id = f"{self._parent._id}-c-{len(self._parent._children)}"
|
||||
self.data["id"] = self._id
|
||||
else:
|
||||
self._id = None
|
||||
|
||||
def create(self):
|
||||
new_child = create("div", self._id, "py-li-element")
|
||||
new_child._element.innerHTML = dedent(
|
||||
f"""
|
||||
<label id="{self._id}" for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox" class="task-check">
|
||||
<p>{self.render_content()}</p>
|
||||
</label>
|
||||
"""
|
||||
)
|
||||
return new_child
|
||||
|
||||
def on_click(self, evt):
|
||||
pass
|
||||
|
||||
def pre_append(self):
|
||||
pass
|
||||
|
||||
def post_append(self):
|
||||
self.element.click = self.on_click
|
||||
self.element.onclick = self.on_click
|
||||
self._post_append()
|
||||
|
||||
def _post_append(self):
|
||||
pass
|
||||
|
||||
def strike(self, value, extra=None):
|
||||
if value:
|
||||
self.add_class("line-through")
|
||||
else:
|
||||
self.remove_class("line-through")
|
||||
|
||||
def render_content(self):
|
||||
return " - ".join([self.data[f] for f in self.labels])
|
||||
|
||||
|
||||
class PyListTemplate:
|
||||
item_class = PyItemTemplate
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self._children = []
|
||||
self._id = self.parent.id
|
||||
self.main_style_classes = "py-li-element"
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
return self._children
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return [c.data for c in self._children]
|
||||
|
||||
def render_children(self):
|
||||
binds = {}
|
||||
for i, c in enumerate(self._children):
|
||||
txt = c.element.innerHTML
|
||||
rnd = str(time.time()).replace(".", "")[-5:]
|
||||
new_id = f"{c.element.id}-{i}-{rnd}"
|
||||
binds[new_id] = c.element.id
|
||||
txt = txt.replace(">", f" id='{new_id}'>")
|
||||
print(txt)
|
||||
|
||||
def foo(evt):
|
||||
evtEl = evt.srcElement
|
||||
srcEl = Element(binds[evtEl.id])
|
||||
srcEl.element.onclick()
|
||||
evtEl.classList = srcEl.element.classList
|
||||
|
||||
for new_id in binds:
|
||||
Element(new_id).element.onclick = foo
|
||||
|
||||
def connect(self):
|
||||
self.md = main_div = js.document.createElement("div")
|
||||
main_div.id = self._id + "-list-tasks-container"
|
||||
|
||||
if self.main_style_classes:
|
||||
for klass in self.main_style_classes.split(" "):
|
||||
main_div.classList.add(klass)
|
||||
|
||||
self.parent.appendChild(main_div)
|
||||
|
||||
def add(self, *args, **kws):
|
||||
if not isinstance(args[0], self.item_class):
|
||||
child = self.item_class(*args, **kws)
|
||||
else:
|
||||
child = args[0]
|
||||
child.register_parent(self)
|
||||
return self._add(child)
|
||||
|
||||
def _add(self, child_elem):
|
||||
self.pre_child_append(child_elem)
|
||||
child_elem.pre_append()
|
||||
self._children.append(child_elem)
|
||||
self.md.appendChild(child_elem.create().element)
|
||||
child_elem.post_append()
|
||||
self.child_appended(child_elem)
|
||||
return child_elem
|
||||
|
||||
def pre_child_append(self, child):
|
||||
pass
|
||||
|
||||
def child_appended(self, child):
|
||||
"""Overwrite me to define logic"""
|
||||
pass
|
||||
|
||||
|
||||
class PyItem(PyItemTemplate):
|
||||
def on_click(self, evt=None):
|
||||
self.data["done"] = not self.data["done"]
|
||||
self.strike(self.data["done"])
|
||||
|
||||
self.select("input").element.checked = self.data["done"]
|
||||
|
||||
|
||||
class PyList(PyListTemplate):
|
||||
item_class = PyItem
|
||||
|
||||
def add(self, item):
|
||||
if isinstance(item, str):
|
||||
item = {"content": item, "done": False, "created_at": dt.now()}
|
||||
|
||||
super().add(item, labels=["content"], state_key="done")
|
||||
|
||||
|
||||
@plugin.register_custom_element("py-list")
|
||||
class PyListPlugin:
|
||||
def __init__(self, element):
|
||||
self.element = element
|
||||
self.py_list = PyList(self.element)
|
||||
|
||||
def add(self, item):
|
||||
self.py_list.add(item)
|
||||
|
||||
def connect(self):
|
||||
self.py_list.connect()
|
||||
@@ -1,21 +0,0 @@
|
||||
from datetime import datetime as dt
|
||||
|
||||
import pyscript
|
||||
|
||||
|
||||
class PyItem(pyscript.PyItemTemplate):
|
||||
def on_click(self, evt=None):
|
||||
self.data["done"] = not self.data["done"]
|
||||
self.strike(self.data["done"])
|
||||
|
||||
self.select("input").element.checked = self.data["done"]
|
||||
|
||||
|
||||
class PyList(pyscript.PyListTemplate):
|
||||
item_class = PyItem
|
||||
|
||||
def add(self, item):
|
||||
if isinstance(item, str):
|
||||
item = {"content": item, "done": False, "created_at": dt.now()}
|
||||
|
||||
super().add(item, labels=["content"], state_key="done")
|
||||
@@ -33,7 +33,7 @@
|
||||
<py-tutor modules="antigravity.py">
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
[[fetch]]
|
||||
files = ["./antigravity.py"]
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<h1 class="font-semibold text-2xl ml-5">Custom REPL</h1>
|
||||
<py-tutor modules="antigravity.py">
|
||||
<py-tutor modules="utils.py;antigravity.py">
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
|
||||
[[fetch]]
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
id="run"
|
||||
type="button"
|
||||
class="button is-primary"
|
||||
py-onClick="run()"
|
||||
py-click="run()"
|
||||
>
|
||||
Run!
|
||||
</button>
|
||||
@@ -86,7 +86,7 @@
|
||||
id="clear"
|
||||
type="button"
|
||||
class="button is-danger"
|
||||
py-onClick="clear()"
|
||||
py-click="clear()"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
|
||||
@@ -12,8 +12,16 @@
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<style>
|
||||
py-script {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -27,25 +35,27 @@
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div class="font-mono">
|
||||
start time: <label id="outputDiv"></label>
|
||||
start time: <label id="output1"></label>
|
||||
</div>
|
||||
<div id="outputDiv2" class="font-mono"></div>
|
||||
<div id="outputDiv3" class="font-mono"></div>
|
||||
<div id="output2" class="font-mono"></div>
|
||||
<div id="output3" class="font-mono"></div>
|
||||
|
||||
<py-tutor modules="utils.py">
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py"
|
||||
]
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
<py-script>
|
||||
<script type="py">
|
||||
import utils
|
||||
display(utils.now())
|
||||
</py-script>
|
||||
from pyscript import display
|
||||
display(utils.now(), target="output1")
|
||||
</script>
|
||||
|
||||
<py-script>
|
||||
from pyscript import display
|
||||
from utils import now
|
||||
import asyncio
|
||||
|
||||
@@ -53,15 +63,14 @@
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
output = now()
|
||||
Element("outputDiv2").write(output)
|
||||
display(output, target="output2")
|
||||
|
||||
out3 = Element("outputDiv3")
|
||||
if output[-1] in ["0", "4", "8"]:
|
||||
out3.write("It's espresso time!")
|
||||
display("It's espresso time!", target="output3")
|
||||
else:
|
||||
out3.clear()
|
||||
display("", target="output3")
|
||||
|
||||
pyscript.run_until_complete(foo())
|
||||
foo()
|
||||
</py-script>
|
||||
</py-tutor>
|
||||
</section>
|
||||
|
||||
@@ -28,33 +28,30 @@
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<h1>To Do List</h1>
|
||||
<py-tutor modules="utils.py;pylist.py">
|
||||
<py-register-widget
|
||||
src="./pylist.py"
|
||||
name="py-list"
|
||||
klass="PyList"
|
||||
></py-register-widget>
|
||||
|
||||
<py-tutor modules="utils.py">
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py",
|
||||
"./py_list.py"
|
||||
]
|
||||
[[fetch]]
|
||||
files = ["./utils.py", "./pylist.py"]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
from js import document
|
||||
from datetime import datetime as dt
|
||||
from pyodide.ffi.wrappers import add_event_listener
|
||||
|
||||
def add_task(*ags, **kws):
|
||||
def add_task(*args, **kws):
|
||||
# create a new dictionary representing the new task
|
||||
new_task_content = Element("new-task-content")
|
||||
task = { "content": new_task_content.value, "done": False, "created_at": dt.now() }
|
||||
|
||||
# add a new task to the list and tell it to use the `content` key to show in the UI
|
||||
# and to use the key `done` to sync the task status with a checkbox element in the UI
|
||||
myList.add(task)
|
||||
myList = Element("myList")
|
||||
myList.element.pyElementInstance.add(task)
|
||||
|
||||
# clear the inputbox element used to create the new task
|
||||
new_task_content.clear()
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -26,13 +29,14 @@
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<py-tutor modules="./utils.py;./todo.py">
|
||||
<py-tutor modules="./todo.py">
|
||||
<py-config>
|
||||
plugins = [
|
||||
"../build/plugins/python/py_tutor.py"
|
||||
"https://pyscript.net/latest/plugins/python/py_tutor.py",
|
||||
"./py_list.py"
|
||||
]
|
||||
[[fetch]]
|
||||
files = ["./utils.py", "./todo.py"]
|
||||
files = ["./todo.py"]
|
||||
</py-config>
|
||||
|
||||
<py-script src="./todo.py"></py-script>
|
||||
@@ -56,7 +60,7 @@
|
||||
id="new-task-btn"
|
||||
class="py-button"
|
||||
type="submit"
|
||||
py-click="add_task()"
|
||||
py-click="add_task"
|
||||
>
|
||||
Add task
|
||||
</button>
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
from datetime import datetime as dt
|
||||
|
||||
from utils import add_class, remove_class
|
||||
from pyscript import document
|
||||
|
||||
tasks = []
|
||||
|
||||
|
||||
def q(selector, root=document):
|
||||
return root.querySelector(selector)
|
||||
|
||||
|
||||
# define the task template that will be use to render new templates to the page
|
||||
task_template = Element("task-template").select(".task", from_content=True)
|
||||
task_list = Element("list-tasks-container")
|
||||
new_task_content = Element("new-task-content")
|
||||
task_template = q("#task-template").content.querySelector(".task")
|
||||
task_list = q("#list-tasks-container")
|
||||
new_task_content = q("#new-task-content")
|
||||
|
||||
|
||||
def add_task(*ags, **kws):
|
||||
def add_task(e):
|
||||
# ignore empty task
|
||||
if not new_task_content.element.value:
|
||||
if not new_task_content.value:
|
||||
return None
|
||||
|
||||
# create task
|
||||
task_id = f"task-{len(tasks)}"
|
||||
task = {
|
||||
"id": task_id,
|
||||
"content": new_task_content.element.value,
|
||||
"content": new_task_content.value,
|
||||
"done": False,
|
||||
"created_at": dt.now(),
|
||||
}
|
||||
@@ -28,26 +33,24 @@ def add_task(*ags, **kws):
|
||||
|
||||
# add the task element to the page as new node in the list by cloning from a
|
||||
# template
|
||||
task_html = task_template.clone(task_id)
|
||||
task_html_content = task_html.select("p")
|
||||
task_html_content.element.innerText = task["content"]
|
||||
task_html_check = task_html.select("input")
|
||||
task_list.element.appendChild(task_html.element)
|
||||
task_html = task_template.cloneNode(True)
|
||||
task_html.id = task_id
|
||||
task_html_check = q("input", root=task_html)
|
||||
task_html_content = q("p", root=task_html)
|
||||
task_html_content.textContent = task["content"]
|
||||
task_list.append(task_html)
|
||||
|
||||
def check_task(evt=None):
|
||||
task["done"] = not task["done"]
|
||||
if task["done"]:
|
||||
add_class(task_html_content, "line-through")
|
||||
else:
|
||||
remove_class(task_html_content, "line-through")
|
||||
task_html_content.classList.toggle("line-through", task["done"])
|
||||
|
||||
new_task_content.clear()
|
||||
task_html_check.element.onclick = check_task
|
||||
new_task_content.value = ""
|
||||
task_html_check.onclick = check_task
|
||||
|
||||
|
||||
def add_task_event(e):
|
||||
if e.key == "Enter":
|
||||
add_task()
|
||||
add_task(e)
|
||||
|
||||
|
||||
new_task_content.element.onkeypress = add_task_event
|
||||
new_task_content.onkeypress = add_task_event
|
||||
|
||||
@@ -10,8 +10,8 @@ def now(fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
|
||||
|
||||
def remove_class(element, class_name):
|
||||
element.element.classList.remove(class_name)
|
||||
element.classList.remove(class_name)
|
||||
|
||||
|
||||
def add_class(element, class_name):
|
||||
element.element.classList.add(class_name)
|
||||
element.classList.add(class_name)
|
||||
|
||||
@@ -21,19 +21,26 @@
|
||||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.147.0/three.min.js"></script>
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/latest/pyscript.css"
|
||||
/>
|
||||
<style>
|
||||
py-script {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
type="module"
|
||||
src="https://esm.sh/@pyscript/core@latest/core.js"
|
||||
></script>
|
||||
<py-script>
|
||||
from pyodide.ffi import create_proxy, to_js
|
||||
from js import window
|
||||
from pyscript import window, document
|
||||
from js import Math
|
||||
from js import THREE
|
||||
from js import performance
|
||||
from js import Object
|
||||
from js import document
|
||||
import asyncio
|
||||
|
||||
mouse = THREE.Vector2.new();
|
||||
|
||||
46
newdocs/Makefile
Normal file
46
newdocs/Makefile
Normal file
@@ -0,0 +1,46 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
CONDA_ENV ?= _env
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
env := $(CONDA_ENV)
|
||||
conda_run := conda run -p $(env)
|
||||
|
||||
setup:
|
||||
@if [ -z "$${CONDA_SHLVL:+x}" ]; then echo "Conda is not installed." && exit 1; fi
|
||||
$(CONDA_EXE) env $(shell [ -d $(env) ] && echo update || echo create) -p $(env) --file environment.yml
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
clean-all: clean
|
||||
rm -rf $(env) *.egg-info
|
||||
|
||||
shell:
|
||||
@export CONDA_ENV_PROMPT='<{name}>'
|
||||
@echo 'conda activate $(env)'
|
||||
|
||||
htmlserve: html
|
||||
@echo 'visit docs at http://localhost:8080'
|
||||
python -m http.server -d "$(BUILDDIR)/html/" 8080
|
||||
|
||||
livehtml:
|
||||
sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
|
||||
.PHONY: help Makefile setup clean clean-all shell
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
56
newdocs/README.md
Normal file
56
newdocs/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# PyScript documentation
|
||||
|
||||
Welcome to the PyScript documentation directory, where you can find
|
||||
and contribute to discussions around PyScript and related topics.
|
||||
|
||||
## Getting started
|
||||
|
||||
Before you start contributing to the documentation, it's worthwhile to
|
||||
take a look at the general contributing guidelines for the PyScript project. You can find these guidelines here
|
||||
[Contributing Guidelines](https://github.com/pyscript/pyscript/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## Documentation Principles
|
||||
|
||||
The PyScript documentation is based on a documentation framework called [Diátaxis](https://diataxis.fr/). This framework helps to solve the problem of structure in technical documentation and identifies four modes of documentation - **tutorials, how-to guides, technical reference and explanation**. Each one of these modes answers to a different user need, fulfills a different purpose and requires a different approach to its creation.
|
||||
|
||||
The picture below gives a good visual representation of that separation of concerns:
|
||||
|
||||

|
||||
|
||||
So, please keep that in mind when contributing to the project documentation. For more information on, make sure to check [their website](https://diataxis.fr/).
|
||||
|
||||
### Setup
|
||||
|
||||
The `docs` directory in the pyscript repository contains a
|
||||
[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) documentation project. Material is a system
|
||||
that takes plaintext files containing documentation written in Markdown, along with
|
||||
static files like templates and themes, to build the static end result.
|
||||
|
||||
To setup the documentation development environment simply run `make setup` from this folder and, once it's done,
|
||||
activate your environment by running `conda activate ./_env`
|
||||
|
||||
### Build
|
||||
|
||||
Simply run `mkdocs serve`
|
||||
|
||||
## Cross-referencing
|
||||
|
||||
You can link to other pages in the documentation by using the `{doc}` role. For example, to link to the `docs/README.md` file, you would use:
|
||||
|
||||
```markdown
|
||||
{doc}`docs/README.md`
|
||||
```
|
||||
|
||||
You can also cross-reference the python glossary by using the `{term}` role. For example, to link to the `iterable` term, you would use:
|
||||
|
||||
```markdown
|
||||
{term}`iterable`
|
||||
```
|
||||
|
||||
You can also cross-reference functions, methods or data attributes by using the `{attr}` for example:
|
||||
|
||||
```markdown
|
||||
{py:func}`repr`
|
||||
```
|
||||
|
||||
This would link to the `repr` function in the python builtins.
|
||||
1
newdocs/docs/advanced-user-guide.md
Normal file
1
newdocs/docs/advanced-user-guide.md
Normal file
@@ -0,0 +1 @@
|
||||
# Advanced User Guide
|
||||
16
newdocs/docs/assets/images/pyscript-black.svg
Normal file
16
newdocs/docs/assets/images/pyscript-black.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg width="100%" viewBox="0 0 2057 974" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#000000" stroke="none" transform="translate(0 100)">
|
||||
<path
|
||||
d="M 1092.534 158.364 C 1095.764 169.589 1102.374 179.795 1107.224 190.364 C 1119.104 216.243 1131.874 241.728 1144.274 267.364 C 1179.204 339.56 1214.064 411.844 1248.314 484.364 C 1260.474 510.112 1273.154 535.617 1285.314 561.364 C 1290.014 571.319 1299.154 583.378 1300.684 594.364 C 1301.444 599.785 1296.944 606.478 1294.984 611.364 C 1289.004 626.289 1282.004 640.557 1273.734 654.364 C 1265.284 668.483 1256.704 683.257 1245.444 695.364 C 1237.304 704.123 1228.664 712.851 1218.534 719.31 C 1176.654 746.023 1130.104 739.811 1084.534 729.364 L 1084.534 796.364 C 1137.744 803.235 1191.744 806.988 1241.534 782.094 C 1291.224 757.25 1321.144 708.125 1345.794 660.364 C 1391.424 571.949 1425.474 477.074 1463.954 385.364 C 1484.774 335.759 1505.144 285.968 1525.954 236.364 C 1532.804 220.048 1539.454 203.643 1546.384 187.364 C 1550.314 178.14 1555.824 168.274 1557.534 158.364 L 1503.534 158.364 C 1498.104 158.364 1487.624 156.363 1482.924 159.392 C 1477.284 163.031 1474.824 176.375 1472.254 182.364 C 1463.294 203.198 1455.174 224.401 1446.524 245.364 C 1422.624 303.289 1398.764 361.248 1375.334 419.364 C 1365.024 444.923 1349.894 471.569 1343.534 498.364 L 1341.534 498.364 L 1326.784 467.364 L 1300.794 414.364 L 1219.784 248.364 L 1188.284 184.364 L 1174.894 159.392 L 1152.534 158.364 L 1092.534 158.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 100.534 391.364 C 109.625 398.897 122.97 403.329 133.534 408.611 L 197.534 440.611 L 405.534 544.611 C 436.606 560.147 467.458 576.073 498.534 591.611 C 511.98 598.334 527.713 609.722 542.534 612.364 L 542.534 563.364 L 541.506 543.754 L 518.534 531.117 L 460.534 502.117 L 307.534 425.117 L 240.534 391.364 L 307.534 358.117 L 459.534 282.611 L 518.534 253.117 L 541.506 240.727 L 542.534 221.364 L 542.534 171.364 C 527.073 174.12 510.565 186.102 496.534 193.117 L 398.534 242.117 L 200.534 341.117 C 167.367 357.701 132.553 372.676 100.534 391.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 1600.534 171.364 L 1600.534 220.364 C 1600.534 225.605 1598.654 235.422 1601.564 239.974 C 1605.194 245.662 1617.614 249.159 1623.534 252.117 L 1680.534 280.611 C 1730.924 305.806 1781.134 331.41 1831.534 356.611 C 1853.974 367.829 1877.404 384.412 1901.534 391.364 L 1901.534 393.364 C 1875.624 400.829 1849.674 418.049 1825.534 430.117 L 1679.534 503.117 C 1661.964 511.903 1644.564 521.567 1626.534 529.364 C 1619.964 532.203 1605.494 536.596 1601.564 542.754 C 1598.654 547.306 1600.534 557.122 1600.534 562.364 L 1600.534 612.364 L 1655.534 585.611 L 1763.534 531.611 L 1947.534 439.611 L 2041.534 392.364 C 2031.474 382.202 2012.324 376.511 1999.534 370.117 L 1907.534 324.117 L 1701.534 221.117 L 1635.534 188.117 C 1624.294 182.495 1612.624 174.847 1600.534 171.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 704.534 384.364 C 704.534 374.13 702.051 360.064 705.503 350.364 C 710.589 336.071 722.183 321.459 731.164 309.364 C 737.516 300.809 743.992 292.429 750.959 284.364 C 786.81 242.863 854.576 189.488 905.519 239.403 C 931.848 265.201 939.204 301.065 941.623 336.364 C 946.631 409.413 926.04 491.22 860.534 532.928 C 811.862 563.917 757.912 556.382 704.534 545.364 Z M 705.534 259.364 L 704.534 259.364 L 704.534 158.364 L 628.534 158.364 L 628.534 789.364 L 704.534 789.364 L 704.534 613.364 C 728.157 613.38 751.915 618.29 775.534 619.325 C 816.206 621.106 857.009 614.508 893.534 596.116 C 989.069 548.011 1025.008 434.77 1024.535 335.364 C 1024.298 285.5 1013.766 232.452 979.364 194.364 C 968.209 182.013 954.851 171.287 940.534 162.816 C 875.388 124.27 794.704 158.21 745.534 207.364 C 730.887 222.007 713.84 240.114 705.534 259.364 Z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
16
newdocs/docs/assets/images/pyscript.svg
Normal file
16
newdocs/docs/assets/images/pyscript.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg width="100%" viewBox="0 0 2057 974" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#fda703" stroke="none" transform="translate(0 100)">
|
||||
<path
|
||||
d="M 1092.534 158.364 C 1095.764 169.589 1102.374 179.795 1107.224 190.364 C 1119.104 216.243 1131.874 241.728 1144.274 267.364 C 1179.204 339.56 1214.064 411.844 1248.314 484.364 C 1260.474 510.112 1273.154 535.617 1285.314 561.364 C 1290.014 571.319 1299.154 583.378 1300.684 594.364 C 1301.444 599.785 1296.944 606.478 1294.984 611.364 C 1289.004 626.289 1282.004 640.557 1273.734 654.364 C 1265.284 668.483 1256.704 683.257 1245.444 695.364 C 1237.304 704.123 1228.664 712.851 1218.534 719.31 C 1176.654 746.023 1130.104 739.811 1084.534 729.364 L 1084.534 796.364 C 1137.744 803.235 1191.744 806.988 1241.534 782.094 C 1291.224 757.25 1321.144 708.125 1345.794 660.364 C 1391.424 571.949 1425.474 477.074 1463.954 385.364 C 1484.774 335.759 1505.144 285.968 1525.954 236.364 C 1532.804 220.048 1539.454 203.643 1546.384 187.364 C 1550.314 178.14 1555.824 168.274 1557.534 158.364 L 1503.534 158.364 C 1498.104 158.364 1487.624 156.363 1482.924 159.392 C 1477.284 163.031 1474.824 176.375 1472.254 182.364 C 1463.294 203.198 1455.174 224.401 1446.524 245.364 C 1422.624 303.289 1398.764 361.248 1375.334 419.364 C 1365.024 444.923 1349.894 471.569 1343.534 498.364 L 1341.534 498.364 L 1326.784 467.364 L 1300.794 414.364 L 1219.784 248.364 L 1188.284 184.364 L 1174.894 159.392 L 1152.534 158.364 L 1092.534 158.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 100.534 391.364 C 109.625 398.897 122.97 403.329 133.534 408.611 L 197.534 440.611 L 405.534 544.611 C 436.606 560.147 467.458 576.073 498.534 591.611 C 511.98 598.334 527.713 609.722 542.534 612.364 L 542.534 563.364 L 541.506 543.754 L 518.534 531.117 L 460.534 502.117 L 307.534 425.117 L 240.534 391.364 L 307.534 358.117 L 459.534 282.611 L 518.534 253.117 L 541.506 240.727 L 542.534 221.364 L 542.534 171.364 C 527.073 174.12 510.565 186.102 496.534 193.117 L 398.534 242.117 L 200.534 341.117 C 167.367 357.701 132.553 372.676 100.534 391.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 1600.534 171.364 L 1600.534 220.364 C 1600.534 225.605 1598.654 235.422 1601.564 239.974 C 1605.194 245.662 1617.614 249.159 1623.534 252.117 L 1680.534 280.611 C 1730.924 305.806 1781.134 331.41 1831.534 356.611 C 1853.974 367.829 1877.404 384.412 1901.534 391.364 L 1901.534 393.364 C 1875.624 400.829 1849.674 418.049 1825.534 430.117 L 1679.534 503.117 C 1661.964 511.903 1644.564 521.567 1626.534 529.364 C 1619.964 532.203 1605.494 536.596 1601.564 542.754 C 1598.654 547.306 1600.534 557.122 1600.534 562.364 L 1600.534 612.364 L 1655.534 585.611 L 1763.534 531.611 L 1947.534 439.611 L 2041.534 392.364 C 2031.474 382.202 2012.324 376.511 1999.534 370.117 L 1907.534 324.117 L 1701.534 221.117 L 1635.534 188.117 C 1624.294 182.495 1612.624 174.847 1600.534 171.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 704.534 384.364 C 704.534 374.13 702.051 360.064 705.503 350.364 C 710.589 336.071 722.183 321.459 731.164 309.364 C 737.516 300.809 743.992 292.429 750.959 284.364 C 786.81 242.863 854.576 189.488 905.519 239.403 C 931.848 265.201 939.204 301.065 941.623 336.364 C 946.631 409.413 926.04 491.22 860.534 532.928 C 811.862 563.917 757.912 556.382 704.534 545.364 Z M 705.534 259.364 L 704.534 259.364 L 704.534 158.364 L 628.534 158.364 L 628.534 789.364 L 704.534 789.364 L 704.534 613.364 C 728.157 613.38 751.915 618.29 775.534 619.325 C 816.206 621.106 857.009 614.508 893.534 596.116 C 989.069 548.011 1025.008 434.77 1024.535 335.364 C 1024.298 285.5 1013.766 232.452 979.364 194.364 C 968.209 182.013 954.851 171.287 940.534 162.816 C 875.388 124.27 794.704 158.21 745.534 207.364 C 730.887 222.007 713.84 240.114 705.534 259.364 Z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
1
newdocs/docs/contributing.md
Normal file
1
newdocs/docs/contributing.md
Normal file
@@ -0,0 +1 @@
|
||||
# Contributing
|
||||
38
newdocs/docs/features.md
Normal file
38
newdocs/docs/features.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# What is PyScript - Features
|
||||
|
||||
PyScript is a software platform that combines the power of the web with the power and simplicity
|
||||
of the most popular programming language in the world to provide an ecosystem that let users create
|
||||
and distribute applications in seconds, while having fun.
|
||||
|
||||
|
||||
## How does it work?
|
||||
|
||||
The PyScript library provides HTML tags for embedding and executing Python code in your browser. PyScript is built using Pyodide, the WebAssembly port of CPython, which is compiled using Emscripten.
|
||||
|
||||
PyScript turns the browser into a code deployment tool that anyone can learn to use.
|
||||
|
||||
Example
|
||||
In this example, we are using the <py-script> HTML tag to generate a Matplotlib figure and display it as an image. Click Preview to see the rendered HTML.
|
||||
|
||||
To try it in your browser, copy the code below into an online HTML editor like W3School’s Tryit Editor, which allows you to modify, run, and even save your code. Watch the video below to see it in action!
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
### Run Python code in the browser
|
||||
|
||||
#### In the main thread
|
||||
|
||||
#### In a web worker
|
||||
|
||||
### Environment Setup
|
||||
|
||||
#### Install Python Packages
|
||||
|
||||
#### Download files and assets
|
||||
|
||||
### Error reporting
|
||||
|
||||
### Pythonic Web APIs
|
||||
|
||||
### Access to Javascript
|
||||
349
newdocs/docs/getting-started.md
Normal file
349
newdocs/docs/getting-started.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# Getting started with PyScript
|
||||
|
||||
To start developing a PyScript, like with most applications development, you need a **development environment** where you
|
||||
write your code, a way to install the programming libraries and dependencies your code needs, and way to build and distribute
|
||||
your application.
|
||||
|
||||
Luckily, PyScript makes many of these steps much easier.
|
||||
|
||||
## Requirements
|
||||
|
||||
To visualize a PyScript application, users only need a modern web browser.
|
||||
|
||||
To distribute a PyScript application, the only requirement is for the application to be hosted somewhere a browser can reach.
|
||||
|
||||
To create a PyScript application, users need a Development Environment, often also called [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment)
|
||||
where they can write their code.
|
||||
|
||||
|
||||
**Note:** The easiest way to get the a full PyScript development environment and application hosting setup in seconds,
|
||||
is to use [pyscript.com](pyscript.com) on your browser. It is a free service that helps users create new projects from
|
||||
pre-created templates already structured using best practices allowing user to edit, preview and deploy their apps with
|
||||
just a link, all in the same place.
|
||||
|
||||
|
||||
### Development Environment
|
||||
|
||||
Like most software platforms, PyScript requires a development environment where the user can write their applications. This
|
||||
means an editor where to edit files, installing all dependencies needed by the application and setting everything up so
|
||||
that the application can be build and distributed. PyScript simplify these aspects for the user, reducing these needs to
|
||||
an editor, a browser and ways to serve your application files.
|
||||
|
||||
PyScript does not require any specific development environment other than a web browser (we recommend using
|
||||
[Chrome](https://www.google.com/chrome/)) and a text editor ([IDE](https://en.wikipedia.org/wiki/Integrated_development_environment))
|
||||
that authors can use to write their applications. Users are free to choose according to their preference. We recommend
|
||||
picking youR favorite browser and IDE, or using pyscript.com (that includes an editor in the browser itself).
|
||||
|
||||
**Note:** If you're using [VSCode](https://code.visualstudio.com/), the
|
||||
[Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
|
||||
can be used to reload the page as you edit the HTML file.
|
||||
|
||||
## Installation
|
||||
|
||||
There is no PyScript specific installation required in your system to start using PyScript in your browser. All you need to do is to
|
||||
simply add a reference in your application code to where your application should get PyScript from.
|
||||
|
||||
If you are not an experienced developer and it all sounds very complicated, don't worry, we'll get you through it in the following steps.
|
||||
|
||||
### Application Development Phases
|
||||
|
||||
Just like with any Web Application, the development lifecycle of an application in 2 phases:
|
||||
|
||||
* **development:** this is where authors write their files and application logic
|
||||
* **deployment:** in order to open your application in a browser, your application files need to be
|
||||
"uploaded" to a web server that is able to share your application with the right format when
|
||||
it's requested by a browser. This is also deferret to as "serving" web page.
|
||||
|
||||
Before we get into the `development` topic and have some fine writing our first applications, let's talk about
|
||||
how to serve a pyscript application.
|
||||
|
||||
## Serving your application
|
||||
|
||||
|
||||
While browsers are also capable of opening files from the users local system, it is not recommended because,
|
||||
for security reasons, browsers will forbid and disable many features when accessing files this way.
|
||||
(In this is the case, you may see your Python code in the text of the webpage, and the
|
||||
[browser developer console](https://balsamiq.com/support/faqs/browserconsole/) may show an
|
||||
error like *"Cross origin requests are only supported for HTTP."*)
|
||||
|
||||
In short, when browsers visualize a web page, they expect them to be served by a
|
||||
web server.
|
||||
|
||||
### Serving your application from your computer
|
||||
|
||||
There are many ways you can initiate a local web server to serve files. We'll only cover one of them,
|
||||
using `Python`.
|
||||
|
||||
Assuming you have `Python` installed in your system, `cd` in your application folder and run
|
||||
the following python command:
|
||||
|
||||
```python
|
||||
python3 -m http.server
|
||||
```
|
||||
|
||||
### Serving your application from a web server
|
||||
|
||||
If there are many ways to serve a web application from your computer, there are many more
|
||||
options on how to serve your application from a hosting service on the internet. We will not cover
|
||||
this in detail and only suggest users to look into:
|
||||
|
||||
* pyscript.com as it's a free service and makes the process of authoring and serving an
|
||||
application almost transparent.
|
||||
* github pages as it's a free service and Github is a very popular service adopted by developers.
|
||||
|
||||
|
||||
For the rest of this documentation, we'll be presenting examples and snippets and host them on
|
||||
pyscript.com.
|
||||
|
||||
|
||||
## Basic Application Concepts
|
||||
|
||||
While we'll cover PyScript concepts and APIs more thoroughly in the PyScript Concepts and PyScript User Guide sections, it's important
|
||||
to understand the basics.
|
||||
|
||||
PyScript is a Software Platform that enables users to write Python Applications that run in the Browser, with a simple and user
|
||||
friendly interface. For this reason, it aims to have a small and intuitive interface that triest to enable users while staying out of
|
||||
the way. In fact, there are 3 main parts of a PyScript application:
|
||||
|
||||
1. **Presentation:** Usually this is managed in a `html` file and is also where we specify that `PyScript`` needs
|
||||
to be loaded into the application.
|
||||
2. **Configuration:** where users can define their dependencies, assets to be downloaded, etc. PyScript configuration
|
||||
files in `TOML` or `JSON` formats
|
||||
3. **Code Logic:** These are typically Python files that host the application code. PyScript allows users to run these
|
||||
through special `html` tags (such as `<script type="py">` or `<py-script>`) properly placed in their `html` file.
|
||||
|
||||
The `html` file acts as the entry point and center of gravity of an application.
|
||||
|
||||
|
||||
## Writing your first PyScript application
|
||||
|
||||
As we hinted earlier, writing a PyScript application means writing a web application that
|
||||
can run code writted in Python (and other languages) on the web. This means that creating
|
||||
PyScript applications starts in a very similar way to web applications: from an `html` file.
|
||||
|
||||
Let's start from the most basic and popular "first application example" possible, a
|
||||
"Hello, world!" application! In this case we will:
|
||||
|
||||
1. Write an `html` file that is the main entry point for our application.
|
||||
2. Load `pyscript` in our application by using: `<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script>`
|
||||
3. Skip a configuration file for our projects and use the default since we won't need to install any additional dependencies.
|
||||
4. Add a `<py-script>` tag to use as entrypoint for our Python code, that will be executed when the page loads.
|
||||
|
||||
**NOTE:** We highly recommend users to reproduce and interact with the examples below on their own on pyscript.com or
|
||||
with their favorite Development Environment setup.
|
||||
|
||||
First, create a new file called `hello.html` and paste in the following content and open it in your web browser.
|
||||
|
||||
**Hint:** In the example below, click on the ➕ icon to read hints about specific sections in the code examples
|
||||
|
||||
```html title="hello_world.html"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My First PyScript APP: Hello World!</title>
|
||||
<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script> <!-- (1) Load PyScript -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- (2) In this case were using the default config, so don't need to specify a `<py-config>` tag -->
|
||||
|
||||
<!-- (3) run the code in the `main.py` file -->
|
||||
<py-script src="main.py"></py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
1. ⚡️ we use a `<script>` tag to load `PyScript` in the `head` of our `HTML` document so it can
|
||||
load as soon as possible
|
||||
2. if needed to install any packages we could load a config in this point, so that any python code
|
||||
can have their dependencies installed before they run
|
||||
3. 🐍 the code in `main.py` will run inside the default `Python` interpreter as soon as it's ready
|
||||
|
||||
and create a new `main.py` file with the following code:
|
||||
|
||||
```python title="main.py"
|
||||
from pyscript import display # (1)
|
||||
print('Hello, World!') # print "Hello, World!" to the console
|
||||
display('Hello, World!') # displays "Hello, World!" in the main page
|
||||
```
|
||||
|
||||
1. pyscript provides the `display` funcition that can be used to display any variable on the page,
|
||||
while the Python `print` statement will automatically print objects on the browser `console`.
|
||||
|
||||
<a href="https://fpliger.pyscriptapps.com/hello-world-minimal-example/latest/" class="md-button" target="_blank">open this example on pyscript.com</a>
|
||||
|
||||
When you open application in your browser, you should see `Hello, World!` printed in your page and in your Javascript Console
|
||||
(if you are new to web development and don't know what it means yet, don't worry,t, we'll get into that later).
|
||||
|
||||
Easy, right?
|
||||
|
||||
### Using files vs. inline code
|
||||
|
||||
In the example above we wrote our Python code for the application logic in a separate file called `main.py`.
|
||||
While this is a best practive and recommended, `PyScript` also allows users to write their code in the
|
||||
`html` file, within the `pyscript` tag. In this case, if we rewrote the same example in a single file using
|
||||
this feature, we'd have the following:
|
||||
|
||||
```html title="hello_world.py"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My First PyScript APP: Hello World!</title>
|
||||
<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script> <!-- Load PyScript -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- In this case were using the default config, so don't need to specify a `<py-config>` tag -->
|
||||
|
||||
<!-- (1) run the code that is defined within the <script type="py"> tag-->
|
||||
<script type="py" src="main.py">
|
||||
from pyscript import display # (1)
|
||||
print('Hello, World!') # print "Hello, World!" to the console
|
||||
display('Hello, World!') # displays "Hello, World!" in the main page
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
1. 🐍 Noticed anything different? Yes, we are passing the python code within the tag itself instead
|
||||
of a separate `main.py` file.
|
||||
|
||||
<a href="https://fpliger.pyscriptapps.com/hello-world-minimal-example/latest/" class="md-button" target="_blank">open this example on pyscript.com</a>
|
||||
|
||||
If you noticed, above we are using `<script type="...">` instead of `<py-script>`. That is another way you
|
||||
can run code logic in PyScript. The reason we are using `script` in this case is that the `<py-script>`
|
||||
does not support inline code due to how the browser treats one vs. the other. For all use cases where
|
||||
the code is defined in a separate file, both tags are equivalent
|
||||
|
||||
**⚠️ Important:** While very convenient, we recommend always defining your code in a separate
|
||||
`.py` file as a best practice for the following reasons:
|
||||
|
||||
* editors don't have good support for inline code
|
||||
* it's really hard to test, lint or QA code define within tags
|
||||
* code can be easily exported
|
||||
* both your `html` and `python` code will be easier to read and better organized
|
||||
|
||||
|
||||
## A more complex example
|
||||
|
||||
Now that we know how you can create a simple 'Hello, World!' example, let's use everything
|
||||
we've learned above see a more complex example.
|
||||
|
||||
### Setting up the base index file
|
||||
|
||||
Just like before, let's create a new `html` file that will contain our application template
|
||||
and interface. We'll call this file `index.html` and add the following content:
|
||||
|
||||
```html title="index.html"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Ice Cream Picker</title>
|
||||
<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script> <!-- (1) Load PyScript -->
|
||||
</head>
|
||||
<body>
|
||||
<py-config src="pyscript.toml"></py-config>
|
||||
|
||||
<!-- (3) run the code in the `main.py` file -->
|
||||
<py-script src="main.py"></py-script>
|
||||
|
||||
<div id="input" style="margin: 20px;">
|
||||
Select your 🍨 flavour: <br/>
|
||||
<input type="radio" id="all" name="flavour" value="ALL">
|
||||
<label for="all"> All 🍧</label>
|
||||
<input type="radio" id="chocolate" name="flavour" value="COCOA">
|
||||
<label for="chocolate"> Chocolate 🍫</label>
|
||||
<input type="radio" id="cherry" name="flavour" value="CHERRIES">
|
||||
<label for="cherry"> Cherries 🍒</label>
|
||||
<input type="radio" id="berries" name="flavour" value="BERRY">
|
||||
<label for="berries"> Berries 🍓</label>
|
||||
<input type="radio" id="cheese" name="flavour" value="CHEESE">
|
||||
<label for="cheese"> Cheese 🧀</label>
|
||||
<input type="radio" id="peanut" name="flavour" value="PEANUT">
|
||||
<label for="peanut"> Peanut 🥜</label>
|
||||
</div>
|
||||
|
||||
<div id="graph-area"></div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
This creates a solid base for our application and we are ready to start adding the elements we need for our application.
|
||||
|
||||
### Importing the needed libraries
|
||||
|
||||
For this example, we will need to install `pandas` and `matplotlib`. We can install libraries using the `<py-config>` tag
|
||||
we already added to the `index.html` file above. All we have to do now is to create the `pyscript.toml` file with the
|
||||
right dependencies:
|
||||
|
||||
```toml title="pyscript.toml"
|
||||
packages = ["matplotlib", "pandas"]
|
||||
```
|
||||
|
||||
For more information on the configuration files, please refer to the [`<py-config>` documentation](../reference/elements/py-config.md)
|
||||
|
||||
### Importing and plotting the data
|
||||
|
||||
Now that we have installed the needed libraries, we can import and explore the data. In this step, we need
|
||||
to create a `<py-script>` tag to import our dependencies, read the data with pandas and then use `py-repl`
|
||||
to explore the data.
|
||||
|
||||
You may want to read the [`<py-script>`](../reference/elements/py-script.md) documentation for more information about it.
|
||||
|
||||
|
||||
```python title="main.py"
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pyodide.http import open_url
|
||||
from pyodide.ffi import create_proxy
|
||||
from pyscript import display
|
||||
import js
|
||||
|
||||
|
||||
url = (
|
||||
"https://raw.githubusercontent.com/Cheukting/pyscript-ice-cream/main/bj-products.csv"
|
||||
)
|
||||
ice_data = pd.read_csv(open_url(url))
|
||||
|
||||
current_selected = []
|
||||
flavour_elements = js.document.getElementsByName("flavour")
|
||||
|
||||
def plot(data):
|
||||
plt.rcParams["figure.figsize"] = (22,20)
|
||||
fig, ax = plt.subplots()
|
||||
bars = ax.barh(data["name"], data["rating"], height=0.7)
|
||||
ax.bar_label(bars)
|
||||
plt.title("Rating of ice cream flavours of your choice")
|
||||
display(fig, target="graph-area", append=False)
|
||||
|
||||
def select_flavour(event):
|
||||
for ele in flavour_elements:
|
||||
if ele.checked:
|
||||
current_selected = ele.value
|
||||
break
|
||||
if current_selected == "ALL":
|
||||
plot(ice_data)
|
||||
else:
|
||||
filter = ice_data.apply(lambda x: ele.value in x["ingredients"], axis=1)
|
||||
plot(ice_data[filter])
|
||||
|
||||
ele_proxy = create_proxy(select_flavour)
|
||||
|
||||
for ele in flavour_elements:
|
||||
if ele.value == "ALL":
|
||||
ele.checked = True
|
||||
current_selected = ele.value
|
||||
ele.addEventListener("change", ele_proxy)
|
||||
|
||||
plot(ice_data)
|
||||
|
||||
```
|
||||
|
||||
<a href="https://fpliger.pyscriptapps.com/hello-world-minimal-example/latest/" class="md-button" target="_blank">open this example on pyscript.com</a>
|
||||
42
newdocs/docs/index.md
Normal file
42
newdocs/docs/index.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# PyScript
|
||||
|
||||

|
||||
|
||||
<h3 style="text-align: center; font-style: italic;">The programming Platform for the 99%</h3>
|
||||
|
||||
---------------------------------------------------------------------------------------------
|
||||
|
||||
<p style="text-align: center; font-style: italic;">Welcome to the PyScript documentation!</p>
|
||||
|
||||
PyScript is a programming platform that allows you to create web applications that run in
|
||||
the browser, using Python. That gives anyone the ability to program without infrastructure
|
||||
barriers and creates some really interesting benefits:
|
||||
|
||||
* **Easy start**: create and share applications in the browser, not installation required
|
||||
* **Simplicity**: Code your apps with a friendly and intuitive language like Python
|
||||
* **Scalability**: since applications run directly in the browser and not on a server somewhere, servers don't need to
|
||||
scale as much if the number of users of an application grows exponentially
|
||||
* **Shareability**: applications can be shared as easily as sharing an URL. Can't get easier than that ;)
|
||||
* **Multi-Platform**: since the browser is the underlying system where PyScript applications run, applications
|
||||
can run anywhere a modern browser is installed, on windows, linux, mac, mobile, or even a Tesla! :)
|
||||
* **Security**: since PyScript runs core in the Browser (via Web Assembly) in a sandbox fashion and the browsers offers
|
||||
a very strict level of security, code never have access files or part of the underlying system without user permission,
|
||||
making it a great option in terms of security.
|
||||
* **User Friendly APIs**: web APIs are very vast and, sometimes, complicated. PyScript offers smaller and more user friendly
|
||||
APIs for the most common use cases while also providing an option to access the full Web APIs as well.
|
||||
|
||||
We hope you'll enjoy the project and create so many incredible things with it! To learn more, consult our documentation.
|
||||
|
||||
## What next?
|
||||
|
||||
Check out our [getting started](getting-started.md) section to learn where to start from or go to our
|
||||
[user guide](user-guide.md) section for a more in-depth coverage of PyScript features.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :fontawesome-brands-html5: __HTML__ for content and structure
|
||||
- :fontawesome-brands-js: __JavaScript__ for interactivity
|
||||
- :fontawesome-brands-css3: __CSS__ for text running out of boxes
|
||||
- :fontawesome-brands-internet-explorer: __Internet Explorer__ ... huh?
|
||||
|
||||
</div>
|
||||
37
newdocs/docs/serving-your-application.md
Normal file
37
newdocs/docs/serving-your-application.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Serving your application
|
||||
|
||||
Now what we have written our first application, it's important talk about how we can access it.
|
||||
|
||||
In the example above, we were able to visualize it by simply opening the local file from our
|
||||
system directly with the browser. While that's a very simple and fast way to open our application,
|
||||
it is not very recommended because browsers will forbid many features when accessing files this way,
|
||||
for security reasons. When this is the case, you may see your Python code in the text of the webpage,
|
||||
and the [browser developer console](https://balsamiq.com/support/faqs/browserconsole/) may show an
|
||||
error like *"Cross origin requests are only supported for HTTP."*
|
||||
|
||||
In short, when browsers visualize a web page, they expect them to be served by a
|
||||
web server. Here are a few options that we can use to fix this issue:
|
||||
|
||||
**NOTE:** If you are an experienced developer and already know how to host and serve files on a [static]
|
||||
web server, feel free to skip to the next section.
|
||||
|
||||
TODO: It seems better to not go too deep into "how to serve a PyScript application" but to point to a dedicated
|
||||
section where we can add more options and actually point to other resources as well.
|
||||
|
||||
### Using pyscript.com
|
||||
|
||||
If you clicked on <a href="https://fpliger.pyscriptapps.com/hello-world-minimal-example/latest/" target="_blank">the link above</a>
|
||||
you've already saw how pyscript.com can be used to host PyScript applications.
|
||||
|
||||
All you need to do is to create a free account and copy or start creating new projects.
|
||||
|
||||
### Using a Local Server
|
||||
|
||||
A very common fix for this is to use a [simple local server](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Tools_and_setup/set_up_a_local_testing_server) to make your html file available to the browser.
|
||||
|
||||
|
||||
If you have python installed on your system, you can use it's basic built-in server for this purpose via the command line.
|
||||
Change the current working directory of your terminal or command line to the folder where your HTML file is stored.
|
||||
From this folder, run `python -m http.server 8080 --bind 127.0.0.1` in your terminal or command line. With the server
|
||||
program running, point your browser to `http://localhost:8080` to view the contents of that folder. (If a file in
|
||||
that folder is called `index.html`, it will be displayed by default.)
|
||||
1
newdocs/docs/user-guide.md
Normal file
1
newdocs/docs/user-guide.md
Normal file
@@ -0,0 +1 @@
|
||||
# User Guide
|
||||
7
newdocs/environment.yml
Normal file
7
newdocs/environment.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
channels:
|
||||
- conda-forge
|
||||
- defaults
|
||||
dependencies:
|
||||
- python=3.9
|
||||
- pip=20.2.2
|
||||
- mkdocs-material=9.2.6
|
||||
45
newdocs/mkdocs.yml
Normal file
45
newdocs/mkdocs.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
site_name: PyScript
|
||||
|
||||
theme:
|
||||
name: material
|
||||
|
||||
logo: assets/images/pyscript-black.svg
|
||||
|
||||
palette:
|
||||
# Palette toggle for automatic mode
|
||||
- media: "(prefers-color-scheme)"
|
||||
primary: orange
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
|
||||
# Palette toggle for light mode
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
primary: orange
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
|
||||
# Palette toggle for dark mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
primary: orange
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
|
||||
features:
|
||||
- content.code.copy
|
||||
- content.code.annotate
|
||||
|
||||
markdown_extensions:
|
||||
- attr_list
|
||||
- md_in_html
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- pymdownx.superfences
|
||||
5
newdocs/robots.txt
Normal file
5
newdocs/robots.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
User-agent: *
|
||||
Disallow: /review/
|
||||
|
||||
Sitemap: https://docs.pyscript.net/sitemap.xml
|
||||
Host: docs.pyscript.net
|
||||
@@ -6,13 +6,12 @@ build-backend = "setuptools.build_meta"
|
||||
dynamic = ["version"]
|
||||
|
||||
[tool.codespell]
|
||||
ignore-words-list = "afterall"
|
||||
skip = "pyscriptjs/node_modules/*,*.js,*.json"
|
||||
|
||||
[tool.ruff]
|
||||
builtins = [
|
||||
"Element",
|
||||
"PyItemTemplate",
|
||||
"PyListTemplate",
|
||||
"pyscript",
|
||||
]
|
||||
ignore = [
|
||||
|
||||
5
pyscript.core/.npmignore
Normal file
5
pyscript.core/.npmignore
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
rollup/
|
||||
test/
|
||||
package-lock.json
|
||||
tsconfig.json
|
||||
203
pyscript.core/LICENSE
Normal file
203
pyscript.core/LICENSE
Normal file
@@ -0,0 +1,203 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) 2022-present, PyScript Development Team
|
||||
|
||||
Originated at Anaconda, Inc. in 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
3
pyscript.core/README.md
Normal file
3
pyscript.core/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @pyscript/core
|
||||
|
||||
We have moved and renamed previous _core_ module as [polyscript](https://github.com/pyscript/polyscript/#readme), which is the base module used in here to build up _PyScript Next_, now hosted in this folder.
|
||||
1
pyscript.core/core.css
Normal file
1
pyscript.core/core.css
Normal file
@@ -0,0 +1 @@
|
||||
py-config,py-script{display:none}
|
||||
2
pyscript.core/core.js
Normal file
2
pyscript.core/core.js
Normal file
File diff suppressed because one or more lines are too long
254
pyscript.core/docs/README.md
Normal file
254
pyscript.core/docs/README.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# PyScript Next
|
||||
|
||||
<sup>A summary of <code>@pyscript/core</code> features</sup>
|
||||
|
||||
### Getting started
|
||||
|
||||
Differently from [pyscript classic](https://github.com/pyscript/pyscript), where "*classic*" is the disambiguation name we use to describe the two versions of the project, `@pyscript/core` is an *ECMAScript Module* with the follow benefits:
|
||||
|
||||
* it doesn't block the page like a regular script, without a `deferred` attribute, would
|
||||
* it allows modularity in the future
|
||||
* it bootstraps itself once but it allows exports via the module
|
||||
|
||||
Accordingly, this is the bare minimum required output to bootstrap *PyScript Next* in your page via a CDN:
|
||||
|
||||
```html
|
||||
<!-- Option 1: based on esm.sh which in turns is jsdlvr -->
|
||||
<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script>
|
||||
|
||||
<!-- Option 2: based on unpkg.com -->
|
||||
<script type="module" src="https://unpkg.com/@pyscript/core"></script>
|
||||
|
||||
<!-- Option X: any CDN that uses npmjs registry should work -->
|
||||
```
|
||||
|
||||
Once the module is loaded, any `<script type="py"></script>` on the page, or any `<py-script>` tag, would automatically run its own code or the file defined as `src` attribute, after bootstrapping the *pyodide* interpreter.
|
||||
|
||||
If no `<script type="py">` or `<py-script>` tag is present, it is still possible to use the module to bootstrap via JS a *Worker*, bypassing the need to bootstrap *pyodide* on the main thread, hence without ever blocking the page.
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { PyWorker } from "https://unpkg.com/@pyscript/core";
|
||||
|
||||
const worker = PyWorker("./code.py", { config: "./config.toml" /* optional */ });
|
||||
</script>
|
||||
```
|
||||
|
||||
#### CSS
|
||||
|
||||
If you are planning to use either `<py-config>` or `<py-script>` tags on the page, where latter case is usually better off with `<script type="py">` instead, you can also use CDNs to land our custom CSS:
|
||||
|
||||
```html
|
||||
<!-- Option 1: based on esm.sh which in turns is jsdlvr -->
|
||||
<link rel="stylesheet" href="https://esm.sh/@pyscript/core@latest/core.css">
|
||||
|
||||
<!-- Option 2: based on unpkg.com -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/@pyscript/core/css">
|
||||
|
||||
<!-- Option X: any CDN that uses npmjs registry should work -->
|
||||
```
|
||||
|
||||
The CSS is needed to avoid seeing content on the page before *PyScript* gets a chance to initialize itself. This means both `py-config` and `py-script` tags will have a `display:none` property which is overwritten by *PyScript* once it initialize each `py-script` custom element.
|
||||
|
||||
Once again, if you use `<script type="py">` instead, you won't need CSS unless you also have a `py-config` on the page, instead of using an external `config` file, defined via the `config` attribute:
|
||||
|
||||
```html
|
||||
<script type="py" config="./config.toml">
|
||||
from pyscript import display
|
||||
|
||||
display("Hello PyScript Next")
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
## Tag attributes API
|
||||
|
||||
Either `<script type="py">` or `<py-script>` can have zero, one or more attributes:
|
||||
|
||||
* **src** if defined, the content of the tag is ignored and the *Python* code in the file will be evaluated instead.
|
||||
* **config** if defined, the code will be evaluated after the configuration has been parsed but this can also be directly *JSON* so that both `config='{"packages":["numpy"]}'` and `config="./config.json"`, or `config="./config.toml"`, would be valid options.
|
||||
* **async** if present, it will run the *Python* code asynchronously.
|
||||
* **worker** if present, it will not bootstrap *pyodide* on the main page, only on the worker file it points at, as in `<script type="py" worker="./worker.py"></script>`. Both `async` and `config` attributes are also available and used to bootstrap the worker as desired.
|
||||
|
||||
Please note that other [polyscript's attributes](https://pyscript.github.io/polyscript/#script-attributes) are available too but their usage is more advanced.
|
||||
|
||||
|
||||
## JS Module API
|
||||
|
||||
The module itself is currently exporting the following utilities:
|
||||
|
||||
* **PyWorker**, which allows to bootstrap a *worker* with *pyodide* and the *pyscript* module available within the code. This callback accepts a file as argument, and an additional, and optional, `options` object literal, able to understand a `config`, which could also be directly a *JS* object literal instead of a JSON string or a file to point at, and `async` which if `true` will run the worker code with top level await enabled. Please note that the returned reference is exactly the same as [the polyscript's XWorker](https://pyscript.github.io/polyscript/#the-xworker-reference), exposing exact same utilities but granting on bootstrap all hooks are in place and the type is always *pyodide*.
|
||||
* **hooks**, which allows plugins to define *ASAP* callbacks or strings that should be executed either in the main thread or the worker before, or after, the code has been executed.
|
||||
|
||||
```js
|
||||
import { hooks } from "https://unpkg.com/@pyscript/core";
|
||||
|
||||
// example
|
||||
hooks.onInterpreterReady.add((utils, element) => {
|
||||
console.lot(element, 'found', 'pyscript is ready');
|
||||
});
|
||||
|
||||
// the hooks namespace
|
||||
({
|
||||
// each function is invoked before or after python gets executed
|
||||
// via: callback(pyScriptUtils, currentElement)
|
||||
/** @type {Set<function>} */
|
||||
onBeforeRun: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onBeforeRunAync: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onAfterRun: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onAfterRunAsync: new Set(),
|
||||
|
||||
// each function is invoked once when PyScript is ready
|
||||
// and for each element via: callback(pyScriptUtils, currentElement)
|
||||
/** @type {Set<function>} */
|
||||
onInterpreterReady: new Set(),
|
||||
|
||||
// each string is prepended or appended to the worker code
|
||||
/** @type {Set<string>} */
|
||||
codeBeforeRunWorker: new Set(),
|
||||
/** @type {Set<string>} */
|
||||
codeBeforeRunWorkerAsync: new Set(),
|
||||
/** @type {Set<string>} */
|
||||
codeAfterRunWorker: new Set(),
|
||||
/** @type {Set<string>} */
|
||||
codeAfterRunWorkerAsync: new Set(),
|
||||
})
|
||||
```
|
||||
|
||||
Please note that a *worker* is a completely different environment and it's not possible, by specifications, to pass a callback to it, which is why worker sets are strings and not functions.
|
||||
|
||||
However, each worker string can use `from pyscript import x, y, z` as that will be available out of the box.
|
||||
|
||||
## PyScript Module API
|
||||
|
||||
The python module offers various utilities in either the main thread or the worker.
|
||||
|
||||
The commonly shared utilities are:
|
||||
|
||||
* **window** in both main and worker, refers to the actual main thread global window context. In classic PyScript that'd be the equivalent of `import js` in the main, which is still available in *PyScript Next*. However, to make code easily portable between main and workers, we decided to offer this named export but please note that in workers, this is still the *main* window, not the worker global context, which would be reachable instead still via `import js`.
|
||||
* **document** in both main and worker, refers to the actual main page `document`. In classic PyScript, this is the equivalent of `from js import document` on the main thread, but this won't ever work in a worker because there is no `document` in there. Fear not though, *PyScript Next* `document` will instead work out of the box, still accessing the main document behind the scene, so that `from pyscript import document` is granted to work in both main and workers seamlessly.
|
||||
* **display** in both main and worker, refers to the good old `display` utility except:
|
||||
* in the *main* it automatically uses the current script `target` to display content
|
||||
* in the *worker* it still needs to know *where* to display content using the `target="dom-id"` named argument, as workers don't get a default target attached
|
||||
* in both main and worker, the `append=Flase` is the *default* behavior, which is a breaking change compared to classic PyScript, but because there is no `Element` with its `write(content)` utility, which would have used that `append=False` behind the scene, we've decided that `false` as append default is more desired, specially after porting most examples in *PyScript Next*, where `append=True` is the exception, not the norm.
|
||||
|
||||
#### Extra main-only features
|
||||
|
||||
* **PyWorker** which allows Python code to create a PyScript worker with the *pyscript* module pre-bundled. Please note that running PyScript on the main requires *pyodide* bootstrap, but also every worker requires *pyodide* bootstrap a part, as each worker is an environment / sandbox a part. This means that using *PyWorker* in the main will take, even if the main interpreter is already up and running, a bit of time to bootstrap the worker, also accordingly to the config files or packages in it.
|
||||
|
||||
|
||||
#### Extra worker-only features
|
||||
|
||||
* **sync** which allows both main and the worker to seamlessly pass any serializable data around, without the need to convert Python dictionaries to JS object literals, as that's done automatically.
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { PyWorker } from "https://unpkg.com/@pyscript/core";
|
||||
|
||||
const worker = PyWorker("./worker.py");
|
||||
|
||||
worker.sync.alert_message = message => {
|
||||
alert(message);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```python
|
||||
from pyscript import sync
|
||||
|
||||
sync.alert_message("Hello Main!")
|
||||
```
|
||||
|
||||
### Worker requirements
|
||||
|
||||
To make it possible to use what looks like *synchronous* DOM APIs, or any other API available via the `window` within a *worker*, we are using latest Web features such as [Atomics](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics).
|
||||
|
||||
Without going into too many details, this means that the *SharedArrayBuffer* primitive must be available, and to do so, the server should enable the following headers:
|
||||
|
||||
```
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Resource-Policy: cross-origin
|
||||
```
|
||||
|
||||
These headers allow local files to be secured and yet able to load resources from the Web (i.e. pyodide library or its packages).
|
||||
|
||||
> ℹ️ **Careful**: we are using and testing these headers on both Desktop and Mobile to be sure all major browsers work as expected (Safari, Firefox, Chromium based browsers). If you change the value of these headers please be sure you test your target devices and browsers properly.
|
||||
|
||||
Please note that if you don't have control over your server's headers, it is possible to simply put [mini-coi](https://github.com/WebReflection/mini-coi#readme) script at the root of your *PyScript with Workers* enabled folder (site root, or any subfolder).
|
||||
|
||||
```sh
|
||||
cd project-folder
|
||||
|
||||
# grab mini-coi content and save it locally as mini-coi.js
|
||||
curl -Ls https://unpkg.com/mini-coi -o mini-coi.js
|
||||
```
|
||||
|
||||
With either these two solutions, it should be now possible to bootstrap a *PyScript Worker* without any issue.
|
||||
|
||||
#### mini-coi example
|
||||
```html
|
||||
<!doctype html>
|
||||
<script src="/mini-coi.js"></script>
|
||||
<script type="module">
|
||||
import { PyWorker } from "https://unpkg.com/@pyscript/core";
|
||||
PyWorker("./test.py");
|
||||
</script>
|
||||
<!-- ./test.py -->
|
||||
<!--
|
||||
from pyscript import document
|
||||
document.body.textContent = "Hello PyScript Worker"
|
||||
-->
|
||||
```
|
||||
|
||||
Please note that a local or remote web server is still needed to allow the Service Worker and `python -m http.server` would do locally, *except* we need to reach `http://localhost:8000/`, not `http://0.0.0.0:8000/`, because the browser does not consider safe non localhost sites when the insecure `http://` protocol, instead of `https://`, is reached.
|
||||
|
||||
|
||||
#### local server example
|
||||
If you'd like to test locally these headers, without needing the *mini-coi* Service Worker, you can use various projects or, if you have *NodeJS* available, simply run the following command in the folder containing the site/project:
|
||||
|
||||
```sh
|
||||
# bootstrap a local server with all headers needed
|
||||
npx static-handler --cors --coep --coop --corp .
|
||||
```
|
||||
|
||||
|
||||
### F.A.Q.
|
||||
|
||||
<details>
|
||||
<summary><strong>why config attribute can also contain JSON but not TOML?</strong></summary>
|
||||
<div markdown=1>
|
||||
|
||||
The *JSON* standard doesn't require new lines or indentation so it felt quick and desired to allow inline JSON as attribute content.
|
||||
|
||||
It's true that HTML attributes can be multi-line too, if properly embedded, but that looked too awkward and definitively harder to explain to me.
|
||||
|
||||
We might decide to allow TOML too in the future, but the direct config as attribute, instead of a proper file, or the usage of `<py-config>`, is meant for quick and simple packages or files dependencies and not much else.
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>why worker attribute needs an external file?</strong></summary>
|
||||
<div markdown=1>
|
||||
|
||||
It would create confusion to have worker code embedded directly in the page and let *PyScript* forward the content to be executed as worker, but the separation of concerns felt more aligned with the meaning of bootstrapping a worker: it inevitably happens elsewhere and with little caveats or features here and there, so it's OK for now to keep that separation explicit.
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>what are the worker's caveats?</strong></summary>
|
||||
<div markdown=1>
|
||||
|
||||
When interacting with `window` or `document` it's important to understand that these use, behind the scene, an orchestrated [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) dance.
|
||||
|
||||
This means that some kind of data that cannot be passed around, specially not compatible with the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
|
||||
|
||||
In short, please try to stick with *JS* references when passing along, or dealing with, *DOM* or other *APIs*.
|
||||
|
||||
</div>
|
||||
</details>
|
||||
1888
pyscript.core/package-lock.json
generated
Normal file
1888
pyscript.core/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
55
pyscript.core/package.json
Normal file
55
pyscript.core/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.1.6",
|
||||
"type": "module",
|
||||
"description": "PyScript",
|
||||
"main": "./core.js",
|
||||
"module": "./core.js",
|
||||
"unpkg": "./core.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/esm/core.d.ts",
|
||||
"import": "./esm/core.js"
|
||||
},
|
||||
"./js": {
|
||||
"import": "./core.js"
|
||||
},
|
||||
"./css": {
|
||||
"import": "./core.css"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"server": "npx static-handler --cors --coep --coop --corp .",
|
||||
"build": "rollup --config rollup/core.config.js && rollup --config rollup/core-css.config.js && npm run ts",
|
||||
"ts": "tsc -p ."
|
||||
},
|
||||
"keywords": [
|
||||
"pyscript",
|
||||
"core"
|
||||
],
|
||||
"author": "Anaconda Inc.",
|
||||
"license": "APACHE-2.0",
|
||||
"dependencies": {
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"basic-devtools": "^0.1.6",
|
||||
"polyscript": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@rollup/plugin-terser": "^0.4.3",
|
||||
"rollup": "^3.28.1",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"static-handler": "^0.4.2",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pyscript/pyscript.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/pyscript/pyscript/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pyscript/pyscript#readme"
|
||||
}
|
||||
20
pyscript.core/rollup/core-css.config.js
Normal file
20
pyscript.core/rollup/core-css.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import postcss from "rollup-plugin-postcss";
|
||||
|
||||
export default {
|
||||
input: "./src/core.css",
|
||||
plugins: [
|
||||
postcss({
|
||||
extract: true,
|
||||
sourceMap: false,
|
||||
minimize: !process.env.NO_MIN,
|
||||
plugins: [],
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
file: "./core.css",
|
||||
},
|
||||
onwarn(warning, warn) {
|
||||
if (warning.code === "FILE_NAME_CONFLICT") return;
|
||||
warn(warning);
|
||||
},
|
||||
};
|
||||
24
pyscript.core/rollup/core.config.js
Normal file
24
pyscript.core/rollup/core.config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// This file generates /core.js minified version of the module, which is
|
||||
// the default exported as npm entry.
|
||||
|
||||
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||
import terser from "@rollup/plugin-terser";
|
||||
import { string } from "rollup-plugin-string";
|
||||
|
||||
const plugins = [
|
||||
string({
|
||||
// Required to be specified
|
||||
include: "**/*.py",
|
||||
}),
|
||||
];
|
||||
|
||||
export default {
|
||||
input: "./src/core.js",
|
||||
plugins: plugins.concat(
|
||||
process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
|
||||
),
|
||||
output: {
|
||||
esModule: true,
|
||||
file: "./core.js",
|
||||
},
|
||||
};
|
||||
4
pyscript.core/src/core.css
Normal file
4
pyscript.core/src/core.css
Normal file
@@ -0,0 +1,4 @@
|
||||
py-script,
|
||||
py-config {
|
||||
display: none;
|
||||
}
|
||||
282
pyscript.core/src/core.js
Normal file
282
pyscript.core/src/core.js
Normal file
@@ -0,0 +1,282 @@
|
||||
import "@ungap/with-resolvers";
|
||||
import { $ } from "basic-devtools";
|
||||
import { define, XWorker } from "polyscript";
|
||||
import { htmlDecode } from "./utils.js";
|
||||
import sync from "./sync.js";
|
||||
|
||||
// this is imported as string (via rollup)
|
||||
import display from "./display.py";
|
||||
|
||||
// TODO: this is not strictly polyscript related but handy ... not sure
|
||||
// we should factor this utility out a part but this works anyway.
|
||||
import { queryTarget } from "../node_modules/polyscript/esm/script-handler.js";
|
||||
import { Hook } from "../node_modules/polyscript/esm/worker/hooks.js";
|
||||
|
||||
import { robustFetch as fetch } from "./fetch.js";
|
||||
|
||||
const { assign, defineProperty } = Object;
|
||||
|
||||
const getText = (body) => body.text();
|
||||
|
||||
// allows lazy element features on code evaluation
|
||||
let currentElement;
|
||||
|
||||
// create a unique identifier when/if needed
|
||||
let id = 0;
|
||||
const getID = (prefix = "py") => `${prefix}-${id++}`;
|
||||
|
||||
// find the shared config for all py-script elements
|
||||
let config;
|
||||
let pyConfig = $("py-config");
|
||||
if (pyConfig) config = pyConfig.getAttribute("src") || pyConfig.textContent;
|
||||
else {
|
||||
pyConfig = $('script[type="py"]');
|
||||
config = pyConfig?.getAttribute("config");
|
||||
}
|
||||
|
||||
if (/^https?:\/\//.test(config)) config = await fetch(config).then(getText);
|
||||
|
||||
// generic helper to disambiguate between custom element and script
|
||||
const isScript = (element) => element.tagName === "SCRIPT";
|
||||
|
||||
// helper for all script[type="py"] out there
|
||||
const before = (script) => {
|
||||
defineProperty(document, "currentScript", {
|
||||
configurable: true,
|
||||
get: () => script,
|
||||
});
|
||||
};
|
||||
|
||||
const after = () => {
|
||||
delete document.currentScript;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a generic DOM Element, tries to fetch the 'src' attribute, if present.
|
||||
* It either throws an error if the 'src' can't be fetched or it returns a fallback
|
||||
* content as source.
|
||||
*/
|
||||
const fetchSource = async (tag, io, asText) => {
|
||||
if (tag.hasAttribute("src")) {
|
||||
try {
|
||||
return await fetch(tag.getAttribute("src")).then(getText);
|
||||
} catch (error) {
|
||||
io.stderr(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (asText) return tag.textContent;
|
||||
|
||||
console.warn(
|
||||
'Deprecated: use <script type="py"> for an always safe content parsing:\n',
|
||||
tag.innerHTML,
|
||||
);
|
||||
|
||||
return htmlDecode(tag.innerHTML);
|
||||
};
|
||||
|
||||
// common life-cycle handlers for any node
|
||||
const bootstrapNodeAndPlugins = (pyodide, element, callback, hook) => {
|
||||
if (isScript(element)) callback(element);
|
||||
for (const fn of hooks[hook]) fn(pyodide, element);
|
||||
};
|
||||
|
||||
const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
||||
// automatically use the pyscript stderr (when/if defined)
|
||||
// this defaults to console.error
|
||||
function PyWorker(...args) {
|
||||
const worker = $XWorker(...args);
|
||||
worker.onerror = ({ error }) => io.stderr(error);
|
||||
return worker;
|
||||
}
|
||||
// trap once the python `display` utility (borrowed from "classic PyScript")
|
||||
// provide the regular Pyodide globals instead of those from xworker
|
||||
const pyDisplay = interpreter.runPython(
|
||||
[
|
||||
"import js as window",
|
||||
"document=window.document",
|
||||
display,
|
||||
"display",
|
||||
].join("\n"),
|
||||
// avoid leaking on global
|
||||
{ globals: interpreter.runPython("{}") },
|
||||
);
|
||||
interpreter.registerJsModule("pyscript", {
|
||||
PyWorker,
|
||||
document,
|
||||
window,
|
||||
// a getter to ensure if multiple scripts with same
|
||||
// env (py) runs, their execution code will have the correct
|
||||
// display reference with automatic target
|
||||
get display() {
|
||||
const id = isScript(currentElement)
|
||||
? currentElement.target.id
|
||||
: currentElement.id;
|
||||
|
||||
return (...args) => {
|
||||
const last = args.at(-1);
|
||||
let kw = { target: id, append: false };
|
||||
if (
|
||||
typeof last === "object" &&
|
||||
last &&
|
||||
("target" in last || "append" in last)
|
||||
)
|
||||
kw = { ...kw, ...args.pop() };
|
||||
pyDisplay.callKwargs(...args, kw);
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const hooks = {
|
||||
/** @type {Set<function>} */
|
||||
onBeforeRun: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onBeforeRunAync: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onAfterRun: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onAfterRunAsync: new Set(),
|
||||
/** @type {Set<function>} */
|
||||
onInterpreterReady: new Set(),
|
||||
|
||||
/** @type {Set<string>} */
|
||||
codeBeforeRunWorker: new Set(),
|
||||
/** @type {Set<string>} */
|
||||
codeBeforeRunWorkerAsync: new Set(),
|
||||
/** @type {Set<string>} */
|
||||
codeAfterRunWorker: new Set(),
|
||||
/** @type {Set<string>} */
|
||||
codeAfterRunWorkerAsync: new Set(),
|
||||
};
|
||||
|
||||
const workerPyScriptModule = [
|
||||
"from pathlib import Path as _Path",
|
||||
`_Path("./pyscript.py").write_text(${JSON.stringify(
|
||||
[
|
||||
"from polyscript import xworker as _xworker",
|
||||
"window=_xworker.window",
|
||||
"document=window.document",
|
||||
"sync=_xworker.sync",
|
||||
display,
|
||||
].join("\n"),
|
||||
)})`,
|
||||
"del _Path",
|
||||
].join("\n");
|
||||
|
||||
const workerHooks = {
|
||||
codeBeforeRunWorker: () =>
|
||||
[workerPyScriptModule, ...hooks.codeBeforeRunWorker].join("\n"),
|
||||
codeBeforeRunWorkerAsync: () =>
|
||||
[workerPyScriptModule, ...hooks.codeBeforeRunWorkerAsync].join("\n"),
|
||||
codeAfterRunWorker: () => [...hooks.codeAfterRunWorker].join("\n"),
|
||||
codeAfterRunWorkerAsync: () =>
|
||||
[...hooks.codeAfterRunWorkerAsync].join("\n"),
|
||||
};
|
||||
|
||||
// define the module as both `<script type="py">` and `<py-script>`
|
||||
define("py", {
|
||||
config,
|
||||
env: "py-script",
|
||||
interpreter: "pyodide",
|
||||
...workerHooks,
|
||||
onWorkerReady(_, xworker) {
|
||||
assign(xworker.sync, sync);
|
||||
},
|
||||
onBeforeRun(pyodide, element) {
|
||||
currentElement = element;
|
||||
bootstrapNodeAndPlugins(pyodide, element, before, "onBeforeRun");
|
||||
},
|
||||
onBeforeRunAync(pyodide, element) {
|
||||
currentElement = element;
|
||||
bootstrapNodeAndPlugins(pyodide, element, before, "onBeforeRunAync");
|
||||
},
|
||||
onAfterRun(pyodide, element) {
|
||||
bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRun");
|
||||
},
|
||||
onAfterRunAsync(pyodide, element) {
|
||||
bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRunAsync");
|
||||
},
|
||||
async onInterpreterReady(pyodide, element) {
|
||||
registerModule(pyodide, element);
|
||||
// allows plugins to do whatever they want with the element
|
||||
// before regular stuff happens in here
|
||||
for (const callback of hooks.onInterpreterReady)
|
||||
callback(pyodide, element);
|
||||
if (isScript(element)) {
|
||||
const {
|
||||
attributes: { async: isAsync, target },
|
||||
} = element;
|
||||
const hasTarget = !!target?.value;
|
||||
const show = hasTarget
|
||||
? queryTarget(target.value)
|
||||
: document.createElement("script-py");
|
||||
|
||||
if (!hasTarget) {
|
||||
const { head, body } = document;
|
||||
if (head.contains(element)) body.append(show);
|
||||
else element.after(show);
|
||||
}
|
||||
if (!show.id) show.id = getID();
|
||||
|
||||
// allows the code to retrieve the target element via
|
||||
// document.currentScript.target if needed
|
||||
defineProperty(element, "target", { value: show });
|
||||
|
||||
pyodide[`run${isAsync ? "Async" : ""}`](
|
||||
await fetchSource(element, pyodide.io, true),
|
||||
);
|
||||
} else {
|
||||
// resolve PyScriptElement to allow connectedCallback
|
||||
element._pyodide.resolve(pyodide);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
class PyScriptElement extends HTMLElement {
|
||||
constructor() {
|
||||
assign(super(), {
|
||||
_pyodide: Promise.withResolvers(),
|
||||
srcCode: "",
|
||||
executed: false,
|
||||
});
|
||||
}
|
||||
get id() {
|
||||
return super.id || (super.id = getID());
|
||||
}
|
||||
set id(value) {
|
||||
super.id = value;
|
||||
}
|
||||
async connectedCallback() {
|
||||
if (!this.executed) {
|
||||
this.executed = true;
|
||||
const { io, run } = await this._pyodide.promise;
|
||||
this.srcCode = await fetchSource(this, io, !this.childElementCount);
|
||||
this.replaceChildren();
|
||||
run(this.srcCode);
|
||||
this.style.display = "block";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("py-script", PyScriptElement);
|
||||
|
||||
/**
|
||||
* A `Worker` facade able to bootstrap on the worker thread only a PyScript module.
|
||||
* @param {string} file the python file to run ina worker.
|
||||
* @param {{config?: string | object, async?: boolean}} [options] optional configuration for the worker.
|
||||
* @returns {Worker & {sync: ProxyHandler<object>}}
|
||||
*/
|
||||
export function PyWorker(file, options) {
|
||||
// this propagates pyscript worker hooks without needing a pyscript
|
||||
// bootstrap + it passes arguments and enforces `pyodide`
|
||||
// as the interpreter to use in the worker, as all hooks assume that
|
||||
// and as `pyodide` is the only default interpreter that can deal with
|
||||
// all the features we need to deliver pyscript out there.
|
||||
const xworker = XWorker.call(new Hook(null, workerHooks), file, {
|
||||
...options,
|
||||
type: "pyodide",
|
||||
});
|
||||
assign(xworker.sync, sync);
|
||||
return xworker;
|
||||
}
|
||||
140
pyscript.core/src/display.py
Normal file
140
pyscript.core/src/display.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# ⚠️ WARNING - both `document` and `window` are added at runtime
|
||||
|
||||
import base64
|
||||
import html
|
||||
import io
|
||||
import re
|
||||
|
||||
|
||||
_MIME_METHODS = {
|
||||
"__repr__": "text/plain",
|
||||
"_repr_html_": "text/html",
|
||||
"_repr_markdown_": "text/markdown",
|
||||
"_repr_svg_": "image/svg+xml",
|
||||
"_repr_png_": "image/png",
|
||||
"_repr_pdf_": "application/pdf",
|
||||
"_repr_jpeg_": "image/jpeg",
|
||||
"_repr_latex": "text/latex",
|
||||
"_repr_json_": "application/json",
|
||||
"_repr_javascript_": "application/javascript",
|
||||
"savefig": "image/png",
|
||||
}
|
||||
|
||||
|
||||
def _render_image(mime, value, meta):
|
||||
# If the image value is using bytes we should convert it to base64
|
||||
# otherwise it will return raw bytes and the browser will not be able to
|
||||
# render it.
|
||||
if isinstance(value, bytes):
|
||||
value = base64.b64encode(value).decode("utf-8")
|
||||
|
||||
# This is the pattern of base64 strings
|
||||
base64_pattern = re.compile(
|
||||
r"^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$"
|
||||
)
|
||||
# If value doesn't match the base64 pattern we should encode it to base64
|
||||
if len(value) > 0 and not base64_pattern.match(value):
|
||||
value = base64.b64encode(value.encode("utf-8")).decode("utf-8")
|
||||
|
||||
data = f"data:{mime};charset=utf-8;base64,{value}"
|
||||
attrs = " ".join(['{k}="{v}"' for k, v in meta.items()])
|
||||
return f'<img src="{data}" {attrs}></img>'
|
||||
|
||||
|
||||
def _identity(value, meta):
|
||||
return value
|
||||
|
||||
|
||||
_MIME_RENDERERS = {
|
||||
"text/plain": html.escape,
|
||||
"text/html": _identity,
|
||||
"image/png": lambda value, meta: _render_image("image/png", value, meta),
|
||||
"image/jpeg": lambda value, meta: _render_image("image/jpeg", value, meta),
|
||||
"image/svg+xml": _identity,
|
||||
"application/json": _identity,
|
||||
"application/javascript": lambda value, meta: f"<script>{value}<\\/script>",
|
||||
}
|
||||
|
||||
|
||||
def _eval_formatter(obj, print_method):
|
||||
"""
|
||||
Evaluates a formatter method.
|
||||
"""
|
||||
if print_method == "__repr__":
|
||||
return repr(obj)
|
||||
elif hasattr(obj, print_method):
|
||||
if print_method == "savefig":
|
||||
buf = io.BytesIO()
|
||||
obj.savefig(buf, format="png")
|
||||
buf.seek(0)
|
||||
return base64.b64encode(buf.read()).decode("utf-8")
|
||||
return getattr(obj, print_method)()
|
||||
elif print_method == "_repr_mimebundle_":
|
||||
return {}, {}
|
||||
return None
|
||||
|
||||
|
||||
def _format_mime(obj):
|
||||
"""
|
||||
Formats object using _repr_x_ methods.
|
||||
"""
|
||||
if isinstance(obj, str):
|
||||
return html.escape(obj), "text/plain"
|
||||
|
||||
mimebundle = _eval_formatter(obj, "_repr_mimebundle_")
|
||||
if isinstance(mimebundle, tuple):
|
||||
format_dict, _ = mimebundle
|
||||
else:
|
||||
format_dict = mimebundle
|
||||
|
||||
output, not_available = None, []
|
||||
for method, mime_type in reversed(_MIME_METHODS.items()):
|
||||
if mime_type in format_dict:
|
||||
output = format_dict[mime_type]
|
||||
else:
|
||||
output = _eval_formatter(obj, method)
|
||||
|
||||
if output is None:
|
||||
continue
|
||||
elif mime_type not in _MIME_RENDERERS:
|
||||
not_available.append(mime_type)
|
||||
continue
|
||||
break
|
||||
if output is None:
|
||||
if not_available:
|
||||
window.console.warn(
|
||||
f"Rendered object requested unavailable MIME renderers: {not_available}"
|
||||
)
|
||||
output = repr(output)
|
||||
mime_type = "text/plain"
|
||||
elif isinstance(output, tuple):
|
||||
output, meta = output
|
||||
else:
|
||||
meta = {}
|
||||
return _MIME_RENDERERS[mime_type](output, meta), mime_type
|
||||
|
||||
|
||||
def _write(element, value, append=False):
|
||||
html, mime_type = _format_mime(value)
|
||||
if html == "\\n":
|
||||
return
|
||||
|
||||
if append:
|
||||
out_element = document.createElement("div")
|
||||
element.append(out_element)
|
||||
else:
|
||||
out_element = element.lastElementChild
|
||||
if out_element is None:
|
||||
out_element = element
|
||||
|
||||
if mime_type in ("application/javascript", "text/html"):
|
||||
script_element = document.createRange().createContextualFragment(html)
|
||||
out_element.append(script_element)
|
||||
else:
|
||||
out_element.innerHTML = html
|
||||
|
||||
|
||||
def display(*values, target=None, append=True):
|
||||
element = document.getElementById(target)
|
||||
for v in values:
|
||||
_write(element, v, append=append)
|
||||
81
pyscript.core/src/exceptions.js
Normal file
81
pyscript.core/src/exceptions.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const CLOSEBUTTON =
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='currentColor' width='12px'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>";
|
||||
|
||||
/**
|
||||
* These error codes are used to identify the type of error that occurred.
|
||||
* @see https://docs.pyscript.net/latest/reference/exceptions.html?highlight=errors
|
||||
*/
|
||||
export const ErrorCode = {
|
||||
GENERIC: "PY0000", // Use this only for development then change to a more specific error code
|
||||
FETCH_ERROR: "PY0001",
|
||||
FETCH_NAME_ERROR: "PY0002",
|
||||
// Currently these are created depending on error code received from fetching
|
||||
FETCH_UNAUTHORIZED_ERROR: "PY0401",
|
||||
FETCH_FORBIDDEN_ERROR: "PY0403",
|
||||
FETCH_NOT_FOUND_ERROR: "PY0404",
|
||||
FETCH_SERVER_ERROR: "PY0500",
|
||||
FETCH_UNAVAILABLE_ERROR: "PY0503",
|
||||
BAD_CONFIG: "PY1000",
|
||||
MICROPIP_INSTALL_ERROR: "PY1001",
|
||||
BAD_PLUGIN_FILE_EXTENSION: "PY2000",
|
||||
NO_DEFAULT_EXPORT: "PY2001",
|
||||
TOP_LEVEL_AWAIT: "PY9000",
|
||||
};
|
||||
|
||||
export class UserError extends Error {
|
||||
constructor(errorCode, message = "", messageType = "text") {
|
||||
super(`(${errorCode}): ${message}`);
|
||||
this.errorCode = errorCode;
|
||||
this.messageType = messageType;
|
||||
this.name = "UserError";
|
||||
}
|
||||
}
|
||||
|
||||
export class FetchError extends UserError {
|
||||
constructor(errorCode, message) {
|
||||
super(errorCode, message);
|
||||
this.name = "FetchError";
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallError extends UserError {
|
||||
constructor(errorCode, message) {
|
||||
super(errorCode, message);
|
||||
this.name = "InstallError";
|
||||
}
|
||||
}
|
||||
|
||||
export function _createAlertBanner(
|
||||
message,
|
||||
level,
|
||||
messageType = "text",
|
||||
logMessage = true,
|
||||
) {
|
||||
switch (`log-${level}-${logMessage}`) {
|
||||
case "log-error-true":
|
||||
console.error(message);
|
||||
break;
|
||||
case "log-warning-true":
|
||||
console.warn(message);
|
||||
break;
|
||||
}
|
||||
|
||||
const content = messageType === "html" ? "innerHTML" : "textContent";
|
||||
const banner = Object.assign(document.createElement("div"), {
|
||||
className: `alert-banner py-${level}`,
|
||||
[content]: message,
|
||||
});
|
||||
|
||||
if (level === "warning") {
|
||||
const closeButton = Object.assign(document.createElement("button"), {
|
||||
id: "alert-close-button",
|
||||
innerHTML: CLOSEBUTTON,
|
||||
});
|
||||
|
||||
banner.appendChild(closeButton).addEventListener("click", () => {
|
||||
banner.remove();
|
||||
});
|
||||
}
|
||||
|
||||
document.body.prepend(banner);
|
||||
}
|
||||
63
pyscript.core/src/fetch.js
Normal file
63
pyscript.core/src/fetch.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { FetchError, ErrorCode } from "./exceptions.js";
|
||||
|
||||
/**
|
||||
* This is a fetch wrapper that handles any non 200 responses and throws a
|
||||
* FetchError with the right ErrorCode. This is useful because our FetchError
|
||||
* will automatically create an alert banner.
|
||||
*
|
||||
* @param {string} url - URL to fetch
|
||||
* @param {Request} [options] - options to pass to fetch
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export async function robustFetch(url, options) {
|
||||
let response;
|
||||
|
||||
// Note: We need to wrap fetch into a try/catch block because fetch
|
||||
// throws a TypeError if the URL is invalid such as http://blah.blah
|
||||
try {
|
||||
response = await fetch(url, options);
|
||||
} catch (err) {
|
||||
const error = err;
|
||||
let errMsg;
|
||||
if (url.startsWith("http")) {
|
||||
errMsg =
|
||||
`Fetching from URL ${url} failed with error ` +
|
||||
`'${error.message}'. Are your filename and path correct?`;
|
||||
} else {
|
||||
errMsg = `Polyscript: Access to local files
|
||||
(using [[fetch]] configurations in <py-config>)
|
||||
is not available when directly opening a HTML file;
|
||||
you must use a webserver to serve the additional files.
|
||||
See <a style="text-decoration: underline;" href="https://github.com/pyscript/pyscript/issues/257#issuecomment-1119595062">this reference</a>
|
||||
on starting a simple webserver with Python.
|
||||
`;
|
||||
}
|
||||
throw new FetchError(ErrorCode.FETCH_ERROR, errMsg);
|
||||
}
|
||||
|
||||
// Note that response.ok is true for 200-299 responses
|
||||
if (!response.ok) {
|
||||
const errorMsg = `Fetching from URL ${url} failed with error ${response.status} (${response.statusText}). Are your filename and path correct?`;
|
||||
switch (response.status) {
|
||||
case 404:
|
||||
throw new FetchError(ErrorCode.FETCH_NOT_FOUND_ERROR, errorMsg);
|
||||
case 401:
|
||||
throw new FetchError(
|
||||
ErrorCode.FETCH_UNAUTHORIZED_ERROR,
|
||||
errorMsg,
|
||||
);
|
||||
case 403:
|
||||
throw new FetchError(ErrorCode.FETCH_FORBIDDEN_ERROR, errorMsg);
|
||||
case 500:
|
||||
throw new FetchError(ErrorCode.FETCH_SERVER_ERROR, errorMsg);
|
||||
case 503:
|
||||
throw new FetchError(
|
||||
ErrorCode.FETCH_UNAVAILABLE_ERROR,
|
||||
errorMsg,
|
||||
);
|
||||
default:
|
||||
throw new FetchError(ErrorCode.FETCH_ERROR, errorMsg);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
5
pyscript.core/src/sync.js
Normal file
5
pyscript.core/src/sync.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
sleep(seconds) {
|
||||
return new Promise(($) => setTimeout($, seconds * 1000));
|
||||
},
|
||||
};
|
||||
6
pyscript.core/src/utils.js
Normal file
6
pyscript.core/src/utils.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const entity = { "<": "<", ">": ">" };
|
||||
const escape = (str) => str.replace(/[<>]/g, (key) => entity[key]);
|
||||
|
||||
export const htmlDecode = (html) =>
|
||||
new DOMParser().parseFromString(escape(html), "text/html").documentElement
|
||||
.textContent;
|
||||
1
pyscript.core/test/a.py
Normal file
1
pyscript.core/test/a.py
Normal file
@@ -0,0 +1 @@
|
||||
print("a")
|
||||
5
pyscript.core/test/config.json
Normal file
5
pyscript.core/test/config.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fetch": [{
|
||||
"files": ["./a.py"]
|
||||
}]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user