diff --git a/micropip/errors.py b/micropip/errors.py new file mode 100644 index 00000000..0ace81fd --- /dev/null +++ b/micropip/errors.py @@ -0,0 +1,24 @@ +""" +A module to define micropip custom Exceptions. +""" + + +class UnsupportedContentTypeError(Exception): + """raise when selecting a parser for current index + + This is raise if the current content type is not recognized. + """ + + +class NoCompatibleWheelError(Exception): + """ + This is raised when a package is found but have no wheel compatible with + current pyodide. + """ + + +class PackageNotFoundOnAnyIndexError(Exception): + """ + This is raised if current package was not found on any of the currently + listed index. + """ diff --git a/micropip/package_index.py b/micropip/package_index.py index f7cf02a6..afd36b1e 100644 --- a/micropip/package_index.py +++ b/micropip/package_index.py @@ -13,6 +13,7 @@ from ._compat import HttpStatusError, fetch_string_and_headers from ._utils import is_package_compatible, parse_version +from .errors import PackageNotFoundOnAnyIndexError, UnsupportedContentTypeError from .externals.mousebender.simple import from_project_details_html from .wheelinfo import WheelInfo @@ -234,7 +235,9 @@ def _select_parser(content_type: str, pkgname: str) -> Callable[[str], ProjectIn ): return partial(ProjectInfo.from_simple_html_api, pkgname=pkgname) case _: - raise ValueError(f"Unsupported content type: {content_type}") + raise UnsupportedContentTypeError( + f"Unsupported content type: {content_type}" + ) async def query_package( @@ -296,14 +299,11 @@ async def query_package( raise content_type = headers.get("content-type", "").lower() - try: - parser = _select_parser(content_type, name) - except ValueError as e: - raise ValueError(f"Error trying to decode url: {url}") from e + parser = _select_parser(content_type, name) return parser(metadata) else: - raise ValueError( - f"Can't fetch metadata for '{name}'. " + raise PackageNotFoundOnAnyIndexError( + f"Can't fetch metadata for {name!r}. " "Please make sure you have entered a correct package name " "and correctly specified index_urls (if you changed them)." ) diff --git a/micropip/transaction.py b/micropip/transaction.py index 9d64a72a..5223af3d 100644 --- a/micropip/transaction.py +++ b/micropip/transaction.py @@ -13,8 +13,9 @@ from ._compat import REPODATA_PACKAGES from ._utils import best_compatible_tag_index, check_compatible from .constants import FAQ_URLS +from .errors import NoCompatibleWheelError from .package import PackageMetadata -from .package_index import ProjectInfo +from .package_index import PackageNotFoundOnAnyIndexError, ProjectInfo from .wheelinfo import WheelInfo logger = logging.getLogger("micropip") @@ -153,12 +154,23 @@ def eval_marker(e: dict[str, str]) -> bool: else: try: await self._add_requirement_from_package_index(req) - except ValueError: - logger.debug( - "Transaction: package %r not found in index, will search lock file", + except PackageNotFoundOnAnyIndexError: + logger.warning( + "Transaction: package %r was not found in any index, " + "falling back to pyodide lock file", + req, + ) + # This is most likely an error, and it's likely we should + # hard fail here, but the legacy behavior was to ignore + # errors. + if not self._add_requirement_from_pyodide_lock(req): + raise + except NoCompatibleWheelError: + logger.warning( + "Transaction: no compatible wheels for package %r was " + "found falling back to pyodide lock file", req, ) - # If the requirement is not found in package index, # we still have a chance to find it from pyodide lockfile. if not self._add_requirement_from_pyodide_lock(req): @@ -167,7 +179,7 @@ def eval_marker(e: dict[str, str]) -> bool: ) raise - except ValueError: + except (NoCompatibleWheelError, PackageNotFoundOnAnyIndexError): self.failed.append(req) if not self.keep_going: raise @@ -292,7 +304,7 @@ def find_wheel(metadata: ProjectInfo, req: Requirement) -> WheelInfo: if best_wheel is not None: return wheel - raise ValueError( + raise NoCompatibleWheelError( f"Can't find a pure Python 3 wheel for '{req}'.\n" f"See: {FAQ_URLS['cant_find_wheel']}\n" "You can use `await micropip.install(..., keep_going=True)` "