Skip to content

Commit

Permalink
Fix #1993 - Expose a handy fetch API (#1994)
Browse files Browse the repository at this point in the history
  • Loading branch information
WebReflection committed Mar 14, 2024
1 parent 69b8884 commit a1268f1
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 0 deletions.
1 change: 1 addition & 0 deletions pyscript.core/src/stdlib/pyscript/__init__.py
Expand Up @@ -30,6 +30,7 @@
# as it works transparently in both the main thread and worker cases.

from pyscript.display import HTML, display
from pyscript.fetch import fetch
from pyscript.magic_js import (
RUNNING_IN_WORKER,
PyWorker,
Expand Down
64 changes: 64 additions & 0 deletions pyscript.core/src/stdlib/pyscript/fetch.py
@@ -0,0 +1,64 @@
import json

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 _Fetch:
def __init__(self, url, **kw):
# avoid both Pyodide and MicroPython FFI
options = js.JSON.parse(json.dumps(kw))
self._url = url
self._fetch = js.fetch(url, options)

async def _arrayBuffer(self):
response = await self._response()
return await response.arrayBuffer()

async def _response(self):
response = await self._fetch
if not response.ok:
msg = f"URL {self._url} failed with status {response.status}"
raise Exception(msg)
return response

# https://developer.mozilla.org/en-US/docs/Web/API/Response/arrayBuffer
# returns a memoryview of the buffer
async def arrayBuffer(self):
buffer = await self._arrayBuffer()
# works in Pyodide
if hasattr(buffer, "to_py"):
return buffer.to_py()
# shims in MicroPython
return memoryview(_as_bytearray(buffer))

# https://developer.mozilla.org/en-US/docs/Web/API/Response/blob
async def blob(self):
response = await self._response()
return await response.blob()

# return a bytearray from the uint8 view of the buffer
async def bytearray(self):
buffer = await self._arrayBuffer()
return _as_bytearray(buffer)

# https://developer.mozilla.org/en-US/docs/Web/API/Response/json
async def json(self):
return json.loads(await self.text())

# https://developer.mozilla.org/en-US/docs/Web/API/Response/text
async def text(self):
response = await self._response()
return await response.text()


def fetch(url, **kw):
return _Fetch(url, **kw)
75 changes: 75 additions & 0 deletions pyscript.core/test/fetch.html
@@ -0,0 +1,75 @@
<!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">
</head>
<body>
<script type="module">
import fetch from 'https://esm.run/@webreflection/fetch';

globalThis.fetch_text = await fetch("config.json").text();
globalThis.fetch_json = JSON.stringify(await fetch("config.json").json());
globalThis.fetch_buffer = new Uint8Array((await fetch("config.json").arrayBuffer())).length;

document.head.appendChild(
Object.assign(
document.createElement('script'),
{
type: 'module',
src: '../dist/core.js'
}
)
);
</script>
<script type="mpy" async>
import js, json
from pyscript import document, fetch

fetch_text = await fetch("config.json").text()
if (fetch_text != js.fetch_text):
raise Exception("fetch_text")

fetch_json = await fetch("config.json").json()
if (json.dumps(fetch_json).replace(" ", "") != js.fetch_json):
raise Exception("fetch_json")

fetch_buffer = await fetch("config.json").bytearray()
if (len(fetch_buffer) != js.fetch_buffer):
raise Exception("fetch_buffer")

print(await fetch("config.json").bytearray())
print(await fetch("config.json").blob())

try:
await fetch("shenanigans.nope").text()
except:
document.documentElement.classList.add('mpy')
</script>
<script type="py" async>
import js, json
from pyscript import document, fetch

fetch_text = await fetch("config.json").text()
if (fetch_text != js.fetch_text):
raise Exception("fetch_text")

fetch_json = await fetch("config.json").json()
if (json.dumps(fetch_json).replace(" ", "") != js.fetch_json):
raise Exception("fetch_json")

fetch_buffer = await fetch("config.json").bytearray()
if (len(fetch_buffer) != js.fetch_buffer):
raise Exception("fetch_buffer")

print(await fetch("config.json").bytearray())
print(await fetch("config.json").blob())

try:
await fetch("shenanigans.nope").text()
except:
document.documentElement.classList.add('py')
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions pyscript.core/test/mpy.spec.js
Expand Up @@ -78,3 +78,8 @@ test('Pyodide + multiple terminals via Worker', async ({ page }) => {
await page.goto('http://localhost:8080/test/py-terminals.html');
await page.waitForSelector('html.first.second');
});

test('MicroPython + Pyodide fetch', async ({ page }) => {
await page.goto('http://localhost:8080/test/fetch.html');
await page.waitForSelector('html.mpy.py');
});
1 change: 1 addition & 0 deletions pyscript.core/types/stdlib/pyscript.d.ts
Expand Up @@ -3,6 +3,7 @@ declare namespace _default {
"__init__.py": string;
"display.py": string;
"event_handling.py": string;
"fetch.py": string;
"magic_js.py": string;
"util.py": string;
};
Expand Down

0 comments on commit a1268f1

Please sign in to comment.