Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make aiohttp work in Jupyterlite #7253

Open
1 task done
MarcSkovMadsen opened this issue Apr 14, 2023 · 12 comments
Open
1 task done

Make aiohttp work in Jupyterlite #7253

MarcSkovMadsen opened this issue Apr 14, 2023 · 12 comments

Comments

@MarcSkovMadsen
Copy link

MarcSkovMadsen commented Apr 14, 2023

Is your feature request related to a problem?

I'm a contributor to Panel which can run on many platforms including Panelite (~Jupyterlite). We would like to be able to use Jupyterlite to provide an easy to use platform to showcase our framework and help our users learn how to use it.

I believe this is a general trend in Python that frameworks and users start moving to the browser powered by Pyodide, PyScript and Jupyterlite for education and for lighter data analytics.

One of Panels reference notebooks shows how to use Panel with async and aiohttp. The problem is that I cannot import aiohttp with piplite.

You can reproduce as described below

import piplite
await piplite.install(['aiohttp'])

image

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 2
      1 import piplite
----> 2 await piplite.install(['aiohttp'])

File /lib/python3.11/site-packages/piplite/piplite.py:103, in _install(requirements, keep_going, deps, credentials, pre)
    101 """Invoke micropip.install with a patch to get data from local indexes"""
    102 with patch("micropip._micropip._get_pypi_json", _get_pypi_json):
--> 103     return await _micropip.install(
    104         requirements=requirements,
    105         keep_going=keep_going,
    106         deps=deps,
    107         credentials=credentials,
    108         pre=pre,
    109     )

File /lib/python3.11/site-packages/micropip/_micropip.py:576, in install(requirements, keep_going, deps, credentials, pre)
    566 wheel_base = Path(getsitepackages()[0])
    568 transaction = Transaction(
    569     ctx=ctx,
    570     ctx_extras=[],
   (...)
    574     fetch_kwargs=fetch_kwargs,
    575 )
--> 576 await transaction.gather_requirements(requirements)
    578 if transaction.failed:
    579     failed_requirements = ", ".join([f"'{req}'" for req in transaction.failed])

File /lib/python3.11/site-packages/micropip/_micropip.py:342, in Transaction.gather_requirements(self, requirements)
    339 for requirement in requirements:
    340     requirement_promises.append(self.add_requirement(requirement))
--> 342 await gather(*requirement_promises)

File /lib/python3.11/site-packages/micropip/_micropip.py:349, in Transaction.add_requirement(self, req)
    346     return await self.add_requirement_inner(req)
    348 if not urlparse(req).path.endswith(".whl"):
--> 349     return await self.add_requirement_inner(Requirement(req))
    351 # custom download location
    352 wheel = WheelInfo.from_url(req)

File /lib/python3.11/site-packages/micropip/_micropip.py:457, in Transaction.add_requirement_inner(self, req)
    452 if self.check_version_satisfied(req):
    453     # Maybe while we were downloading pypi_json some other branch
    454     # installed the wheel?
    455     return
--> 457 await self.add_wheel(wheel, req.extras)

File /lib/python3.11/site-packages/micropip/_micropip.py:472, in Transaction.add_wheel(self, wheel, extras)
    470 await wheel.download(self.fetch_kwargs)
    471 if self.deps:
--> 472     await self.gather_requirements(wheel.requires(extras))
    474 self.wheels.append(wheel)

File /lib/python3.11/site-packages/micropip/_micropip.py:342, in Transaction.gather_requirements(self, requirements)
    339 for requirement in requirements:
    340     requirement_promises.append(self.add_requirement(requirement))
--> 342 await gather(*requirement_promises)

File /lib/python3.11/site-packages/micropip/_micropip.py:346, in Transaction.add_requirement(self, req)
    344 async def add_requirement(self, req: str | Requirement) -> None:
    345     if isinstance(req, Requirement):
--> 346         return await self.add_requirement_inner(req)
    348     if not urlparse(req).path.endswith(".whl"):
    349         return await self.add_requirement_inner(Requirement(req))

File /lib/python3.11/site-packages/micropip/_micropip.py:444, in Transaction.add_requirement_inner(self, req)
    441 metadata = await _get_pypi_json(req.name, self.fetch_kwargs)
    443 try:
--> 444     wheel = find_wheel(metadata, req)
    445 except ValueError:
    446     self.failed.append(req)

File /lib/python3.11/site-packages/micropip/_micropip.py:312, in find_wheel(metadata, req)
    309     if best_wheel is not None:
    310         return wheel
--> 312 raise ValueError(
    313     f"Can't find a pure Python 3 wheel for '{req}'.\n"
    314     f"See: {FAQ_URLS['cant_find_wheel']}\n"
    315     "You can use `micropip.install(..., keep_going=True)`"
    316     "to get a list of all packages with missing wheels."
    317 )

ValueError: Can't find a pure Python 3 wheel for 'multidict<5.0,>=4.5'.
See: https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel
You can use `micropip.install(..., keep_going=True)`to get a list of all packages with missing wheels.

Describe the solution you'd like

That I can import and use aiohttp in Pyodide and Jupyterlite.

Describe alternatives you've considered

Removing the aiohttp notebook from the Panelite example notebooks.

Related component

Client

Additional context

When the import problem has been solved there is probably a lot more to solve to get aiohttp working.

The code we are using is

async def get_img(index):
    async with aiohttp.ClientSession() as session:
        async with session.get(f"https://picsum.photos/800/300?image={index}") as resp:
            return await resp.read()

I can see that multidict can be imported by it self. Its the version pinned that is the import problem.

Code of Conduct

  • I agree to follow the aio-libs Code of Conduct
@Dreamsorcerer
Copy link
Member

Pin in 3.9 is < 7, so if it's just multidict version, this'll be resolved then.

@MarcSkovMadsen
Copy link
Author

Thx. My guess is that more is needed to make aiohttp work in Pyodide and Jupyterlite.

For example there is quite some work in pyodide-http for widely used http libraries to make them work in the browser.

@6r17
Copy link

6r17 commented Apr 16, 2023

I'm not really into the idea of modifying the inner workings of a robust library such as aiohttp so "it works" with another library. For all I know, it should not be aiohttp to change it's behavior.

@MarcSkovMadsen
Copy link
Author

MarcSkovMadsen commented Apr 16, 2023

Pyodide/ webassembly is not another library. Its a new platform for Python. Similar to Windows, OSX and Linux 👍

The ask is not to make it work with Panel. The ask is to also support the browser as a platform vis Pyodide/ PyScript/ Jupyterlite/ webassembly.

I understand this is not a high priority right now.

@6r17
Copy link

6r17 commented Apr 16, 2023

My bad i wrongly understood the request. I'm just a user tough ;)
It's much clearer however !

@Dreamsorcerer
Copy link
Member

I understand this is not a high priority right now.

Generally, if you want a feature, you'll likely have to do the work to implement it. Once it's merged in, we'll try and keep it maintained in the future. I'll try and get a beta release of 3.9 out soon, and then you can retest installation.

@joemarshall
Copy link

@Dreamsorcerer I was considering doing a patch to this library in pyodide-http. Would you have an interest in support directly in aiohttp? Because of the nature of webasembly/emscripten, it would only be client, and would have some limitations due to only being able to do whatever the browser running it does, but looking at the API it doesn't seem so far removed from async http in webasembly. It would be handy for me to push it here rather than to have to monkeypatch it in pyodide-http.

@Dreamsorcerer
Copy link
Member

It depends on what the changes involve really. Can you throw together a draft PR without putting much time into it? Then we can decide on whether it'll be worth finishing off.

@rth rth mentioned this issue Nov 7, 2023
2 tasks
@rth
Copy link

rth commented Nov 7, 2023

FIY @hoodmane wrote a draft monkeypatch in pyodide/pyodide#4282 (comment) and that PR woulds build a binary pyodide/wasm wheel for aiohttp, on which it could be applied.

@hoodmane
Copy link

hoodmane commented Nov 7, 2023

@Dreamsorcerer currently the patch for Pyodide might be a bit invasive. The main problem is that we cannot meaningfully respect the user's choice of Connector. Mostly people seem to pass a TCPConnector but in my interpretation they are more after the arguments they pass to it which control various settings. These settings all need to be reimplemented but timeouts can work in close to the same way.

I think the best thing to do would be to add a Pyodide-specific FetchConnector which is backed by a fetch handler (a function which implements the JavaScript fetch API). The default behavior could be to use globalThis.fetch but you could specify an alternative handler. Then in Pyodide if someone asks for a TCPConnector we rearrange it to be backed by FetchConnector in some hopefully not too invasive way. If they ask for a subclass of BaseConnector that isn't a subclass of FetchConnector or TCPConnector we could just raise.

@hoodmane
Copy link

hoodmane commented Nov 7, 2023

I will try to make a draft PR as a point of discussion, maybe later this week.

@hoodmane
Copy link

hoodmane commented Nov 8, 2023

Okay opened #7803.

hoodmane added a commit to pyodide/pyodide that referenced this issue Nov 18, 2023
This doesn't actually work without an additional patch but it's possible
to get it to work by monkey patching a method. 

See discussion about adding support for Pyodide to aiohttp:
aio-libs/aiohttp#7253
aio-libs/aiohttp#7803
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants