diff --git a/pyscript.core/src/stdlib/pyscript/__init__.py b/pyscript.core/src/stdlib/pyscript/__init__.py index dcd42f62..8e3c53ee 100644 --- a/pyscript.core/src/stdlib/pyscript/__init__.py +++ b/pyscript.core/src/stdlib/pyscript/__init__.py @@ -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, diff --git a/pyscript.core/src/stdlib/pyscript/fetch.py b/pyscript.core/src/stdlib/pyscript/fetch.py new file mode 100644 index 00000000..087f4898 --- /dev/null +++ b/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) diff --git a/pyscript.core/test/fetch.html b/pyscript.core/test/fetch.html new file mode 100644 index 00000000..2c10413a --- /dev/null +++ b/pyscript.core/test/fetch.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + + diff --git a/pyscript.core/test/mpy.spec.js b/pyscript.core/test/mpy.spec.js index b8889102..c1efc70b 100644 --- a/pyscript.core/test/mpy.spec.js +++ b/pyscript.core/test/mpy.spec.js @@ -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'); +}); diff --git a/pyscript.core/types/stdlib/pyscript.d.ts b/pyscript.core/types/stdlib/pyscript.d.ts index 6bff2b5e..47116fa5 100644 --- a/pyscript.core/types/stdlib/pyscript.d.ts +++ b/pyscript.core/types/stdlib/pyscript.d.ts @@ -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; };