8000 Fix #1993 - Expose a handy fetch API (#1994) · woxtu/pyscript@a1268f1 · GitHub
[go: up one dir, main page]

Skip to content

Commit a1268f1

Browse files
Fix pyscript#1993 - Expose a handy fetch API (pyscript#1994)
1 parent 69b8884 commit a1268f1

File tree

5 files changed

+146
-0
lines changed

5 files changed

+146
-0
lines changed

pyscript.core/src/stdlib/pyscript/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
# as it works transparently in both the main thread and worker cases.
3131

3232
from pyscript.display import HTML, display
33+
from pyscript.fetch import fetch
3334
from pyscript.magic_js import (
3435
RUNNING_IN_WORKER,
3536
PyWorker,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import json
2+
3+
import js
4+
5+
6+
def _as_bytearray(buffer):
7+
ui8a = js.Uint8Array.new(buffer)
8+
size = ui8a.length
9+
ba = bytearray(size)
10+
for i in range(0, size):
11+
ba[i] = ui8a[i]
12+
return ba
13+
14+
15+
class _Fetch:
16+
def __init__(self, url, **kw):
17+
# avoid both Pyodide and MicroPython FFI
18+
options = js.JSON.parse(json.dumps(kw))
19+
self._url = url
20+
self._fetch = js.fetch(url, options)
21+
22+
async def _arrayBuffer(self):
23+
response = await self._response()
24+
return await response.arrayBuffer()
25+
26+
async def _response(self):
27+
response = await self._fetch
28+
if not response.ok:
29+
msg = f"URL {self._url} failed with status {response.status}"
30+
raise Exception(msg)
31+
return response
32+
33+
# https://developer.mozilla.org/en-US/docs/Web/API/Response/arrayBuffer
34+
# returns a memoryview of the buffer
35+
async def arrayBuffer(self):
36+
buffer = await self._arrayBuffer()
37+
# works in Pyodide
38+
if hasattr(buffer, "to_py"):
39+
return buffer.to_py()
40+
# shims in MicroPython
41+
return memoryview(_as_bytearray(buffer))
42+
43+
# https://developer.mozilla.org/en-US/docs/Web/API/Response/blob
44+
async def blob(self):
45+
response = await self._response()
46+
return await response.blob()
47+
48+
# return a bytearray from the uint8 view of the buffer
49+
async def bytearray(self):
50+
buffer = await self._arrayBuffer()
51+
return _as_bytearray(buffer)
52+
53+
# https://developer.mozilla.org/en-US/docs/Web/API/Response/json
54+
async def json(self):
55+
return json.loads(await self.text())
56+
57+
# https://developer.mozilla.org/en-US/docs/Web/API/Response/text
58+
async def text(self):
59+
response = await self._response()
60+
return await response.text()
61+
62+
63+
def fetch(url, **kw):
64+
return _Fetch(url, **kw)

pyscript.core/test/fetch.html

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<link rel="stylesheet" href="../dist/core.css">
7+
</head>
8+
<body>
9+
<script type="module">
10+
import fetch from 'https://esm.run/@webreflection/fetch';
11+
12+
globalThis.fetch_text = await fetch("config.json").text();
13+
globalThis.fetch_json = JSON.stringify(await fetch("config.json").json());
14+
globalThis.fetch_buffer = new Uint8Array((await fetch("config.json").arrayBuffer())).length;
15+
16+
document.head.appendChild(
17+
Object.assign(
18+
document.createElement('script'),
19+
{
20+
type: 'module',
21+
src: '../dist/core.js'
22+
}
23+
)
24+
);
25+
</script>
26+
<script type="mpy" async>
27+
import js, json
28+
from pyscript import document, fetch
29+
30+
fetch_text = await fetch("config.json").text()
31+
if (fetch_text != js.fetch_text):
32+
raise Exception("fetch_text")
33+
34+
fetch_json = await fetch("config.json").json()
35+
if (json.dumps(fetch_json).replace(" ", "") != js.fetch_json):
36+
raise Exception("fetch_json")
37+
38+
fetch_buffer = await fetch("config.json").bytearray()
39+
if (len(fetch_buffer) != js.fetch_buffer):
40+
raise Exception("fetch_buffer")
41+
42+
print(await fetch("config.json").bytearray())
43+
print(await fetch("config.json").blob())
44+
45+
try:
46+
await fetch("shenanigans.nope").text()
47+
except:
48+
document.documentElement.classList.add('mpy')
49+
</script>
50+
<script type="py" async>
51+
import js, json
52+
from pyscript import document, fetch
53+
54+
fetch_text = await fetch("config.json").text()
55+
if (fetch_text != js.fetch_text):
56+
raise Exception("fetch_text")
57+
58+
fetch_json = await fetch("config.json").json()
59+
if (json.dumps(fetch_json).replace(" ", "") != js.fetch_json):
60+
raise Exception("fetch_json")
61+
62+
fetch_buffer = await fetch("config.json").bytearray()
63+
if (len(fetch_buffer) != js.fetch_buffer):
64+
raise Exception("fetch_buffer")
65+
66+
print(await fetch("config.json").bytearray())
67+
print(await fetch("config.json").blob())
68+
69+
try:
70+
await fetch("shenanigans.nope").text()
71+
except:
72+
document.documentElement.classList.add('py')
73+
</script>
74+
</body>
75+
</html>

pyscript.core/test/mpy.spec.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,8 @@ test('Pyodide + multiple terminals via Worker', async ({ page }) => {
7878
await page.goto('http://localhost:8080/test/py-terminals.html');
7979
await page.waitForSelector('html.first.second');
8080
});
81+
82+
test('MicroPython + Pyodide fetch', async ({ page }) => {
83+
await page.goto('http://localhost:8080/test/fetch.html');
84+
await page.waitForSelector('html.mpy.py');
85+
});

pyscript.core/types/stdlib/pyscript.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ declare namespace _default {
33
"__init__.py": string;
44
"display.py": string;
55
"event_handling.py": string;
6+
"fetch.py": string;
67
"magic_js.py": string;
78
"util.py": string;
89
};

0 commit comments

Comments
 (0)
0