ucbds-infra / otter-grader

A Python and R autograding solution
https://otter-grader.readthedocs.io
BSD 3-Clause "New" or "Revised" License
126 stars 66 forks source link

Error in JupyterLite with Otter v6 #875

Closed spring-haru closed 2 weeks ago

spring-haru commented 2 weeks ago

Describe the bug I create a notebook in Jupyter Noteobok, and students use JupyterLite. Chronologically:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 2
      1 import piplite
----> 2 await piplite.install('otter-grader')
      3 import otter
      4 grader = otter.Notebook("homework-demo.ipynb")

File /lib/python3.12/site-packages/piplite/piplite.py:117, in _install(requirements, keep_going, deps, credentials, pre, index_urls, verbose)
    115 """Invoke micropip.install with a patch to get data from local indexes"""
    116 with patch("micropip.package_index.query_package", _query_package):
--> 117     return await micropip.install(
    118         requirements=requirements,
    119         keep_going=keep_going,
    120         deps=deps,
    121         credentials=credentials,
    122         pre=pre,
    123         index_urls=index_urls,
    124         verbose=verbose,
    125     )

File /lib/python3.12/site-packages/micropip/_commands/install.py:142, in install(requirements, keep_going, deps, credentials, pre, index_urls, verbose)
    130     index_urls = package_index.INDEX_URLS[:]
    132 transaction = Transaction(
    133     ctx=ctx,
    134     ctx_extras=[],
   (...)
    140     index_urls=index_urls,
    141 )
--> 142 await transaction.gather_requirements(requirements)
    144 if transaction.failed:
    145     failed_requirements = ", ".join([f"'{req}'" for req in transaction.failed])

File /lib/python3.12/site-packages/micropip/transaction.py:55, in Transaction.gather_requirements(self, requirements)
     52 for requirement in requirements:
     53     requirement_promises.append(self.add_requirement(requirement))
---> 55 await asyncio.gather(*requirement_promises)

File /lib/python3.12/site-packages/micropip/transaction.py:62, in Transaction.add_requirement(self, req)
     59     return await self.add_requirement_inner(req)
     61 if not urlparse(req).path.endswith(".whl"):
---> 62     return await self.add_requirement_inner(Requirement(req))
     64 # custom download location
     65 wheel = WheelInfo.from_url(req)

File /lib/python3.12/site-packages/micropip/transaction.py:151, in Transaction.add_requirement_inner(self, req)
    148     if self._add_requirement_from_pyodide_lock(req):
    149         return
--> 151     await self._add_requirement_from_package_index(req)
    152 else:
    153     try:

File /lib/python3.12/site-packages/micropip/transaction.py:198, in Transaction._add_requirement_from_package_index(self, req)
    195 if satisfied:
    196     logger.info(f"Requirement already satisfied: {req} ({ver})")
--> 198 await self.add_wheel(wheel, req.extras, specifier=str(req.specifier))

File /lib/python3.12/site-packages/micropip/transaction.py:236, in Transaction.add_wheel(self, wheel, extras, specifier)
    234 await wheel.download(self.fetch_kwargs)
    235 if self.deps:
--> 236     await self.gather_requirements(wheel.requires(extras))
    238 self.wheels.append(wheel)

File /lib/python3.12/site-packages/micropip/transaction.py:55, in Transaction.gather_requirements(self, requirements)
     52 for requirement in requirements:
     53     requirement_promises.append(self.add_requirement(requirement))
---> 55 await asyncio.gather(*requirement_promises)

File /lib/python3.12/site-packages/micropip/transaction.py:59, in Transaction.add_requirement(self, req)
     57 async def add_requirement(self, req: str | Requirement) -> None:
     58     if isinstance(req, Requirement):
---> 59         return await self.add_requirement_inner(req)
     61     if not urlparse(req).path.endswith(".whl"):
     62         return await self.add_requirement_inner(Requirement(req))

File /lib/python3.12/site-packages/micropip/transaction.py:151, in Transaction.add_requirement_inner(self, req)
    148     if self._add_requirement_from_pyodide_lock(req):
    149         return
--> 151     await self._add_requirement_from_package_index(req)
    152 else:
    153     try:

File /lib/python3.12/site-packages/micropip/transaction.py:190, in Transaction._add_requirement_from_package_index(self, req)
    182 """
    183 Find requirement from package index. If the requirement is found,
    184 add it to the package list and return True. Otherwise, return False.
    185 """
    186 metadata = await package_index.query_package(
    187     req.name, self.fetch_kwargs, index_urls=self.index_urls
    188 )
--> 190 wheel = find_wheel(metadata, req)
    192 # Maybe while we were downloading pypi_json some other branch
    193 # installed the wheel?
    194 satisfied, ver = self.check_version_satisfied(req)

File /lib/python3.12/site-packages/micropip/transaction.py:281, in find_wheel(metadata, req)
    278     if best_wheel is not None:
    279         return wheel
--> 281 raise ValueError(
    282     f"Can't find a pure Python 3 wheel for '{req}'.\n"
    283     f"See: {FAQ_URLS['cant_find_wheel']}\n"
    284     "You can use `await micropip.install(..., keep_going=True)` "
    285     "to get a list of all packages with missing wheels."
    286 )

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

To Reproduce Steps to reproduce the behavior:

Use Otter v5.6.0 to create a notebook for students, and use it in JupyterLite with the following code:

import piplite
await piplite.install('otter-grader')
import otter

No such error occurs If the code is replaced with

%pip install -q otter-grader==5.6.0
import otter

Expected behavior No such error

Versions Python: 3.12.1 Otter: 6.0.0 or 6.0.1

Additional context None

chrispyles commented 2 weeks ago

This should be fixed in v6.0.2