PyScript を使ってみた
python で javascript みたいにフロントエンドをいじれます
github pages にもデプロイできるので、簡単なものなら無料で無限にアプリを作れます!
しかし読み込みがとても遅い...特に scikit-learn を読み込むととてつもない時間がかかってしまう...
だが!Pythonでフロントが書けてしまうWASMの進化に目が離せません!今後はサーバーを立てなくても簡単な機械学習ならフロント側で処理できる時代が来そうですね
初期設定
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> <script defer src="https://pyscript.net/latest/pyscript.js"></script>
※ body の中に書き込む
<body> <py-config> # 必要なパッケージがあればこの中に書き込む... </py-config> <py-script> # この中に処理を書き込む... </py-script> </body>
パッケージのインストール
<py-config> packages = ["matplotlib", "pandas"] </py-config>
自作プラグインのインストール方法
下記のどちらでもいけたが、どちらが良いのかは不明
<py-config> [[fetch]] files = ["/request.py"] </py-config>
または
<py-config> plugins = ["./request.py"] </py-config>
実際の例
# 適当に作った関数 def test_func1(x): return x + 10
<py-config> plugins = ["./pyscript_test.py"] </py-config> <py-script> from pyscript_test import test_func1 result = test_func1(23) display(result) </py-script>
文字の表示
<py-script> def func1(n): display(f'{n}: Hello KEI!') for x in range(3): func1(x) </py-script>
現在時刻を表示
<py-script> from datetime import datetime now = datetime.now() display(now.strftime("%m/%d/%Y, %H:%M:%S")) </py-script>
要素の id を指定して書き込む
<div id="python_output" style="background-color: lightblue"></div> <py-script> x = 500 pyscript.write('python_output', x) </py-script>
ボタンの操作受付
ボタンを押すと現在時刻を取得する例
<button py-click="current_time()" id="get-time" class="py-button">Get current time</button> <p id="current-time"></p> <py-script> from pyscript import Element import datetime def current_time(): now = datetime.datetime.now() # 書き込む 要素のid を指定する paragraph = Element("current-time") # 現在時刻を指定した要素へ書き込む paragraph.write(now.strftime("%Y-%m-%d %H:%M:%S")) </py-script>
ボタンを押すと Hello World を表示
<div id="manual-write"></div> <button py-click="write_to_page()" id="manual">Say Hello</button> <div id="display-write"></div> <button py-click="display_to_div()" id="display">Say Things!</button> <py-script> def write_to_page(): manual_div = Element("manual-write") manual_div.element.innerHTML = "<p><b>Hello World</b></p>" def display_to_div(): display("I display things!", target="display-write") </py-script>
応用例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> <script defer src="https://pyscript.net/latest/pyscript.js"></script> </head> <body> <form onsubmit="return false"> <label for="name">Name:</label><br> <input type="text" id="name" name="name" value="Ethan Hunt"><br> <select name="countries" id="countries"> <option value="India">India</option> <option value="Germany">Germany</option> <option value="Netherlands">Netherlands</option> </select> <button py-click="sub()" type="submit" id="btn-form">submit</button> </form> <p>Output:</p> <p id = 'output'></p> <py-script> def sub(): result_place = Element('output') result_place.write(f"{Element('name').value} is a good human from {Element('countries').value}") </py-script> </body> </html>
非同期通信のやり方
<py-script> import asyncio async def main(): for i in range(3): print(i) await asyncio.sleep(1) asyncio.ensure_future(main()) </py-script>
Fetch
まずは request.py
を作成する。
pyodide
というライブラリによって javascript の fetch を使用できるようになるらしい。
これによって GET, POST, PUT, DELETE を使用できるようになる。
from pyodide.http import pyfetch, FetchResponse from typing import Optional, Any async def request(url: str, method: str = "GET", body: Optional[str] = None, headers: Optional[dict[str, str]] = None, **fetch_kwargs: Any) -> FetchResponse: """ Async request function. Pass in Method and make sure to await! Parameters: url: str = URL to make request to method: str = {"GET", "POST", "PUT", "DELETE"} from `JavaScript` global fetch()) body: str = body as json string. Example, body=json.dumps(my_dict) headers: dict[str, str] = header as dict, will be converted to string... Example, headers=json.dumps({"Content-Type": "application/json"}) fetch_kwargs: Any = any other keyword arguments to pass to `pyfetch` (will be passed to `fetch`) Return: response: pyodide.http.FetchResponse = use with .status or await.json(), etc. """ kwargs = {"method": method, "mode": "cors"} # CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing if body and method not in ["GET", "HEAD"]: kwargs["body"] = body if headers: kwargs["headers"] = headers kwargs.update(fetch_kwargs) response = await pyfetch(url, **kwargs) return response
次に html 側で実装
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>GET, POST, PUT, DELETE example</title> <link rel="icon" type="image/png" href="favicon.png" /> <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> <script defer src="https://pyscript.net/latest/pyscript.js"></script> <py-config> [[fetch]] files = ["/request.py"] </py-config> </head> <body><p> Hello world request example! <br> Here is the output of your request: </p> <py-script> import asyncio import json from request import request # import our request function. async def main(): baseurl = "https://jsonplaceholder.typicode.com" # GET headers = {"Content-type": "application/json"} response = await request(f"{baseurl}/posts/2", method="GET", headers=headers) print(f"GET request=> status:{response.status}, json:{await response.json()}") # POST body = json.dumps({"title": "test_title", "body": "test body", "userId": 1}) new_post = await request(f"{baseurl}/posts", body=body, method="POST", headers=headers) print(f"POST request=> status:{new_post.status}, json:{await new_post.json()}") # PUT body = json.dumps({"id": 1, "title": "test_title", "body": "test body", "userId": 2}) new_post = await request(f"{baseurl}/posts/1", body=body, method="PUT", headers=headers) print(f"PUT request=> status:{new_post.status}, json:{await new_post.json()}") # DELETE new_post = await request(f"{baseurl}/posts/1", method="DELETE", headers=headers) print(f"DELETE request=> status:{new_post.status}, json:{await new_post.json()}") asyncio.ensure_future(main()) </py-script> <div> <p> You can also use other methods. See fetch documentation: <br> https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters </p> </div> <div> <p> See pyodide documentation for what to do with a FetchResponse object: <br> https://pyodide.org/en/stable/usage/api/python-api.html#pyodide.http.FetchResponse </p> </div> </body> </html>