mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
* Fix #2031 - Add pyscript.WebSocket to the mix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Working on a test case anyone can run * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5b4e8527da
commit
1a05ea5fd2
139
pyscript.core/package-lock.json
generated
139
pyscript.core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.4.24",
|
||||
"version": "0.4.25",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.4.24",
|
||||
"version": "0.4.25",
|
||||
"license": "APACHE-2.0",
|
||||
"dependencies": {
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
@@ -29,6 +29,7 @@
|
||||
"@webreflection/toml-j0.4": "^1.1.3",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"bun": "^1.1.6",
|
||||
"chokidar": "^3.6.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"eslint": "^9.1.1",
|
||||
@@ -376,6 +377,110 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@oven/bun-darwin-aarch64": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.1.6.tgz",
|
||||
"integrity": "sha512-1GQb0gugo/qndj5ehktgHRAoyCM5fWisbLEBLq6YorHTc71T5cqFWCQ6uPcfy3k9cxEDd8tUpejekyi8EsCxgw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-darwin-x64": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.1.6.tgz",
|
||||
"integrity": "sha512-NK8py4eJNkN6iNfvaM8pzmmNNNkbyzRvnc/pmrDLFPPfgJklJ2esm8OBCALDe9RvP1USDo8H1I4IXCtQQIu3SQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-darwin-x64-baseline": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.1.6.tgz",
|
||||
"integrity": "sha512-amG6o1y1ksjwOUKS97IW5VQE/UPfEa4Edket1+VUa0UGvo/0z3+2gVYF3Bt+pZA45glJKXM3nI9wYlmVeE/AcQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-linux-aarch64": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.1.6.tgz",
|
||||
"integrity": "sha512-FOlZBeIrk7DlOsktWwahmxJwaKxCG9C+pKPMj8s+u08WhAKtfMBpgHvHsilBKE9W1twta+jB+geW4kU1KZSNZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-linux-x64": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.1.6.tgz",
|
||||
"integrity": "sha512-PN/5XSPCWCuIgOEhxWfx5ViVEY9ZfpEpJkadDiZi9IY17mb/aZVvt0Lll4doe6B7hjbglb1nI++GXcpqU/fonw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-linux-x64-baseline": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.1.6.tgz",
|
||||
"integrity": "sha512-a/Wx5ZTl0BDq9WH5nJfl8rw0YX5/dWmxmy3pYki6EBbwjcsA8iMzjkRpp5k8NEV2SpyaDMdS+xCb/HiaO4RWoQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-windows-x64": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.1.6.tgz",
|
||||
"integrity": "sha512-knJePhYbqtYv5RNUrnkMFrK4z3dDIPzom402yjSLkESZhsnO16xtLkULocV2yk0KDXKo2C3BqoZKHATCK6LxqQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@oven/bun-windows-x64-baseline": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.1.6.tgz",
|
||||
"integrity": "sha512-8VVPdKTYEsQPwlCasLWmTsHPcC9om9FaZlH9j7Gs9jBYje3FDXMyLI3DalVQ/VBY+uwQ8qoXhtDrh83nLuawhA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.43.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz",
|
||||
@@ -938,6 +1043,36 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bun": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/bun/-/bun-1.1.6.tgz",
|
||||
"integrity": "sha512-0oKx5dVZ8LpsAvXdW2DTR5Zt3KS81Ifjx9T2ma7RjDPKbvGIlDXOPt++tr1SndxhFNFsMFeE4i6VKvlL7IR0Zw==",
|
||||
"cpu": [
|
||||
"arm64",
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"os": [
|
||||
"darwin",
|
||||
"linux",
|
||||
"win32"
|
||||
],
|
||||
"bin": {
|
||||
"bun": "bin/bun.exe",
|
||||
"bunx": "bin/bun.exe"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@oven/bun-darwin-aarch64": "1.1.6",
|
||||
"@oven/bun-darwin-x64": "1.1.6",
|
||||
"@oven/bun-darwin-x64-baseline": "1.1.6",
|
||||
"@oven/bun-linux-aarch64": "1.1.6",
|
||||
"@oven/bun-linux-x64": "1.1.6",
|
||||
"@oven/bun-linux-x64-baseline": "1.1.6",
|
||||
"@oven/bun-windows-x64": "1.1.6",
|
||||
"@oven/bun-windows-x64-baseline": "1.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.4.24",
|
||||
"version": "0.4.25",
|
||||
"type": "module",
|
||||
"description": "PyScript",
|
||||
"module": "./index.js",
|
||||
@@ -26,7 +26,8 @@
|
||||
"build:stdlib": "node rollup/stdlib.cjs",
|
||||
"build:3rd-party": "node rollup/3rd-party.cjs",
|
||||
"clean:3rd-party": "rm src/3rd-party/*.js && rm src/3rd-party/*.css",
|
||||
"test:mpy": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test --fully-parallel test/ || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
|
||||
"test:mpy": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test --fully-parallel test/mpy.spec.js || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
|
||||
"test:ws": "bun test/ws/index.js & playwright test test/ws.spec.js",
|
||||
"dev": "node dev.cjs",
|
||||
"release": "npm run build && npm run zip",
|
||||
"size": "echo -e \"\\033[1mdist/*.js file size\\033[0m\"; for js in $(ls dist/*.js); do cat $js | brotli > ._; echo -e \"\\033[2m$js:\\033[0m $(du -h --apparent-size ._ | sed -e 's/[[:space:]]*._//')\"; rm ._; done",
|
||||
@@ -60,6 +61,7 @@
|
||||
"@webreflection/toml-j0.4": "^1.1.3",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"bun": "^1.1.6",
|
||||
"chokidar": "^3.6.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"eslint": "^9.1.1",
|
||||
|
||||
@@ -41,6 +41,7 @@ from pyscript.magic_js import (
|
||||
sync,
|
||||
window,
|
||||
)
|
||||
from pyscript.websocket import WebSocket
|
||||
|
||||
try:
|
||||
from pyscript.event_handling import when
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
|
||||
import js
|
||||
from pyscript.util import as_bytearray
|
||||
|
||||
|
||||
### wrap the response to grant Pythonic results
|
||||
@@ -12,14 +13,6 @@ class _Response:
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self._response, attr)
|
||||
|
||||
def _as_bytearray(self, buffer):
|
||||
ui8a = js.Uint8Array.new(buffer)
|
||||
size = ui8a.length
|
||||
ba = bytearray(size)
|
||||
for i in range(0, size):
|
||||
ba[i] = ui8a[i]
|
||||
return ba
|
||||
|
||||
# exposed methods with Pythonic results
|
||||
async def arrayBuffer(self):
|
||||
buffer = await self._response.arrayBuffer()
|
||||
@@ -27,14 +20,14 @@ class _Response:
|
||||
if hasattr(buffer, "to_py"):
|
||||
return buffer.to_py()
|
||||
# shims in MicroPython
|
||||
return memoryview(self._as_bytearray(buffer))
|
||||
return memoryview(as_bytearray(buffer))
|
||||
|
||||
async def blob(self):
|
||||
return await self._response.blob()
|
||||
|
||||
async def bytearray(self):
|
||||
buffer = await self._response.arrayBuffer()
|
||||
return self._as_bytearray(buffer)
|
||||
return as_bytearray(buffer)
|
||||
|
||||
async def json(self):
|
||||
return json.loads(await self.text())
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
import js
|
||||
|
||||
|
||||
def as_bytearray(buffer):
|
||||
ui8a = js.Uint8Array.new(buffer)
|
||||
size = ui8a.length
|
||||
ba = bytearray(size)
|
||||
for i in range(0, size):
|
||||
ba[i] = ui8a[i]
|
||||
return ba
|
||||
|
||||
|
||||
class NotSupported:
|
||||
"""
|
||||
Small helper that raises exceptions if you try to get/set any attribute on
|
||||
|
||||
64
pyscript.core/src/stdlib/pyscript/websocket.py
Normal file
64
pyscript.core/src/stdlib/pyscript/websocket.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import js
|
||||
from pyscript.util import as_bytearray
|
||||
|
||||
code = "code"
|
||||
protocols = "protocols"
|
||||
reason = "reason"
|
||||
|
||||
|
||||
class EventMessage:
|
||||
def __init__(self, event):
|
||||
self._event = event
|
||||
|
||||
def __getattr__(self, attr):
|
||||
value = getattr(self._event, attr)
|
||||
|
||||
if attr == "data" and not isinstance(value, str):
|
||||
if hasattr(value, "to_py"):
|
||||
return value.to_py()
|
||||
# shims in MicroPython
|
||||
return memoryview(as_bytearray(value))
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class WebSocket(object):
|
||||
CONNECTING = 0
|
||||
OPEN = 1
|
||||
CLOSING = 2
|
||||
CLOSED = 3
|
||||
|
||||
def __init__(self, **kw):
|
||||
url = kw["url"]
|
||||
socket = None
|
||||
if protocols in kw:
|
||||
socket = js.WebSocket.new(url, kw[protocols])
|
||||
else:
|
||||
socket = js.WebSocket.new(url)
|
||||
object.__setattr__(self, "_ws", socket)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self._ws, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr == "onmessage":
|
||||
setattr(self._ws, attr, lambda e: value(EventMessage(e)))
|
||||
else:
|
||||
setattr(self._ws, attr, value)
|
||||
|
||||
def close(self, **kw):
|
||||
if code in kw and reason in kw:
|
||||
self._ws.close(kw[code], kw[reason])
|
||||
elif code in kw:
|
||||
self._ws.close(kw[code])
|
||||
else:
|
||||
self._ws.close()
|
||||
|
||||
def send(self, data):
|
||||
if isinstance(data, str):
|
||||
self._ws.send(data)
|
||||
else:
|
||||
buffer = js.Uint8Array.new(len(data))
|
||||
for pos, b in enumerate(data):
|
||||
buffer[pos] = b
|
||||
self._ws.send(buffer)
|
||||
6
pyscript.core/test/ws.spec.js
Normal file
6
pyscript.core/test/ws.spec.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('MicroPython WebSocket', async ({ page }) => {
|
||||
await page.goto('http://localhost:5037/');
|
||||
await page.waitForSelector('html.ok');
|
||||
});
|
||||
31
pyscript.core/test/ws/index.html
Normal file
31
pyscript.core/test/ws/index.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../dist/core.css">
|
||||
<script type="module" src="../../dist/core.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="mpy" worker>
|
||||
from pyscript import WebSocket, document
|
||||
|
||||
def onopen(event):
|
||||
print(event.type)
|
||||
ws.send("hello")
|
||||
|
||||
def onmessage(event):
|
||||
print(event.type, event.data)
|
||||
ws.close()
|
||||
|
||||
def onclose(event):
|
||||
print(event.type)
|
||||
document.documentElement.classList.add("ok")
|
||||
|
||||
ws = WebSocket(url="ws://localhost:5037/")
|
||||
ws.onopen = onopen
|
||||
ws.onmessage = onmessage
|
||||
ws.onclose = onclose
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
33
pyscript.core/test/ws/index.js
Normal file
33
pyscript.core/test/ws/index.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { serve, file } from 'bun';
|
||||
|
||||
import path, { dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const dir = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
serve({
|
||||
port: 5037,
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) return;
|
||||
const url = new URL(req.url);
|
||||
let { pathname } = url;
|
||||
if (pathname === '/') pathname = '/index.html';
|
||||
else if (/^\/dist\//.test(pathname)) pathname = `/../..${pathname}`;
|
||||
else if (pathname === '/favicon.ico')
|
||||
return new Response('Not Found', { status: 404 });
|
||||
const response = new Response(file(`${dir}${pathname}`));
|
||||
const { headers } = response;
|
||||
headers.set('Cross-Origin-Opener-Policy', 'same-origin');
|
||||
headers.set('Cross-Origin-Embedder-Policy', 'require-corp');
|
||||
headers.set('Cross-Origin-Resource-Policy', 'cross-origin');
|
||||
return response;
|
||||
},
|
||||
websocket: {
|
||||
message(ws, message) {
|
||||
ws.send(message);
|
||||
},
|
||||
close() {
|
||||
process.exit(0);
|
||||
}
|
||||
},
|
||||
});
|
||||
1
pyscript.core/types/stdlib/pyscript.d.ts
vendored
1
pyscript.core/types/stdlib/pyscript.d.ts
vendored
@@ -7,6 +7,7 @@ declare namespace _default {
|
||||
"ffi.py": string;
|
||||
"magic_js.py": string;
|
||||
"util.py": string;
|
||||
"websocket.py": string;
|
||||
};
|
||||
let pyweb: {
|
||||
"__init__.py": string;
|
||||
|
||||
Reference in New Issue
Block a user