1

I am facing an error of "ImportError: cannot import name 'fetch' from 'js' (unknown location)" whenever I try to install any module in pyodide. Initially, I tried the following lines of code :

await pyodide.loadPackage('micropip')
await pyodide.runPythonAsync(`
    import micropip
    await micropip.install('textblob')
    from textblob import TextBlob
    // ... something
    
`)

but it still gave the above error. then i tried to run the code from the official documentation (https://pyodide.org/en/stable/usage/loading-packages.html)

await pyodide.loadPackage("micropip");
        await pyodide.runPythonAsync(`
        import micropip
        micropip.install('snowballstemmer')
        import snowballstemmer
        stemmer = snowballstemmer.stemmer('english')
        print(stemmer.stemWords('go goes going gone'.split()))
      `);

but it still gave me the same error. could someone guide me how to solve this? i also tried to loadpackage('js') and importing it inside the runPythonAsync but it still failed.

here's the full version of the error :-

PythonError: Traceback (most recent call last):
  File "/lib/python3.9/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.9/asyncio/tasks.py", line 258, in __step
    result = coro.throw(exc)
  File "/lib/python3.9/site-packages/micropip/_micropip.py", line 185, in install
    transaction = await self.gather_requirements(requirements, ctx, keep_going)
  File "/lib/python3.9/site-packages/micropip/_micropip.py", line 175, in gather_requirements
    await gather(*requirement_promises)
  File "/lib/python3.9/asyncio/futures.py", line 284, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.9/asyncio/tasks.py", line 328, in __wakeup
    future.result()
  File "/lib/python3.9/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.9/asyncio/tasks.py", line 256, in __step
    result = coro.send(None)
  File "/lib/python3.9/site-packages/micropip/_micropip.py", line 284, in add_requirement
    metadata = await _get_pypi_json(req.name)
  File "/lib/python3.9/site-packages/micropip/_micropip.py", line 88, in _get_pypi_json
    return json.loads(await fetch_string(url))
  File "/lib/python3.9/site-packages/micropip/_micropip.py", line 60, in fetch_string
    return await (await pyfetch(url, **kwargs)).string()
  File "/lib/python3.9/site-packages/pyodide/http.py", line 229, in pyfetch
    from js import fetch as _jsfetch, Object
ImportError: cannot import name 'fetch' from 'js' (unknown location)

guidance needed.

  • I was not able to reproduce in the REPL: https://pyodide.org/en/stable/console.html. Can you make a reproducible example ? – Psidom Feb 27 '22 at 07:12

1 Answers1

0

If you are trying to achieve this in node.js you need to install node-fetch (or any other library that provides fetch function) and make it available as global/globalThis element so that pyodide's js can also access it.

For your example from the documentation regarding snowballstemmer, I was able to run it with the following code inside a file named example.mjs and executing it as node example.mjs:

import fetch from "node-fetch";
globalThis.fetch = fetch;

let pyodide_pkg = await import("./pyodide/pyodide.js");

let pyodide = await pyodide_pkg.loadPyodide({
  indexURL: "pyodide",
});

await pyodide.loadPackage("micropip");

await pyodide.runPythonAsync(`
  import micropip
  await micropip.install('snowballstemmer')
  import snowballstemmer
  stemmer = snowballstemmer.stemmer('english')
  print(stemmer.stemWords('go goes going gone'.split()))
`);

This gives the expected output and the stemmed results as:

['go', 'goe', 'go', 'gone']

PS: While it is a bad practice to use global/globalThis in general but since fetch in browsers is available as a member of window (i.e. window.fetch), I guess it is okay to use it this way.