Skip to content

Commit

Permalink
Example of what we could do to index pip-installed packages
Browse files Browse the repository at this point in the history
This isn't very efficient, we should use pep 658 metadata which pypi provides
now so we don't have to redownload the wheel. Also we should do fetches
concurrently and asyncio.gather them. But it gets the basic concept.
  • Loading branch information
hoodmane committed May 24, 2024
1 parent 43e55d4 commit ea728b6
Showing 1 changed file with 100 additions and 24 deletions.
124 changes: 100 additions & 24 deletions micropip/_commands/freeze.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,110 @@
import importlib.metadata
import json
import sys
from copy import deepcopy
from typing import Any
from importlib.metadata import Distribution
from typing import TypedDict

from packaging.utils import canonicalize_name
from packaging.version import Version

from .._compat import REPODATA_INFO, REPODATA_PACKAGES
from .._utils import fix_package_dependencies
from ..package_index import query_package

IN_VENV = sys.prefix != sys.base_prefix


class PkgEntry(TypedDict):
name: str
version: str
file_name: str
install_dir: str
sha256: str | None
imports: list[str]
depends: list[str]


def get_pkg_entry_micropip(dist: Distribution, url: str) -> PkgEntry:
name = dist.name
version = dist.version
sha256 = dist.read_text("PYODIDE_SHA256")
assert sha256
imports = (dist.read_text("top_level.txt") or "").split()
requires = dist.read_text("PYODIDE_REQUIRES")
if not requires:
fix_package_dependencies(name)
requires = dist.read_text("PYODIDE_REQUIRES")
if requires:
depends = json.loads(requires)
else:
depends = []

return dict(
name=name,
version=version,
file_name=url,
install_dir="site",
sha256=sha256,
imports=imports,
depends=depends,
)


async def get_pkg_entry_pip(dist: Distribution) -> PkgEntry | None:
resp = await query_package(dist.name)
ver = resp.releases.get(Version(dist.version), None)
if ver is None:
return None
wheel = next(ver)
await wheel.download({})
requires = [req.name for req in wheel.requires(set())]
return dict(
name=dist.name,
version=dist.version,
file_name=wheel.url,
install_dir="site",
sha256=wheel.sha256,
imports=[],
depends=requires,
)


async def freeze2() -> str:
"""Produce a json string which can be used as the contents of the
``repodata.json`` lock file.
If you later load Pyodide with this lock file, you can use
:js:func:`pyodide.loadPackage` to load packages that were loaded with :py:mod:`micropip`
this time. Loading packages with :js:func:`~pyodide.loadPackage` is much faster
and you will always get consistent versions of all your dependencies.
You can use your custom lock file by passing an appropriate url to the
``lockFileURL`` of :js:func:`~globalThis.loadPyodide`.
"""
packages = deepcopy(REPODATA_PACKAGES)
for dist in importlib.metadata.distributions():
name = dist.name
url = dist.read_text("PYODIDE_URL")
if url:
pkg_entry = get_pkg_entry_micropip(dist, url)
elif IN_VENV:
res = await get_pkg_entry_pip(dist)
if not res:
continue
pkg_entry = res
else:
continue

packages[canonicalize_name(name)] = pkg_entry

# Sort
packages = dict(sorted(packages.items()))
package_data = {
"info": REPODATA_INFO,
"packages": packages,
}
return json.dumps(package_data)


def freeze() -> str:
Expand All @@ -24,32 +122,10 @@ def freeze() -> str:
packages = deepcopy(REPODATA_PACKAGES)
for dist in importlib.metadata.distributions():
name = dist.name
version = dist.version
url = dist.read_text("PYODIDE_URL")
if url is None:
continue

sha256 = dist.read_text("PYODIDE_SHA256")
assert sha256
imports = (dist.read_text("top_level.txt") or "").split()
requires = dist.read_text("PYODIDE_REQUIRES")
if not requires:
fix_package_dependencies(name)
requires = dist.read_text("PYODIDE_REQUIRES")
if requires:
depends = json.loads(requires)
else:
depends = []

pkg_entry: dict[str, Any] = dict(
name=name,
version=version,
file_name=url,
install_dir="site",
sha256=sha256,
imports=imports,
depends=depends,
)
pkg_entry = get_pkg_entry_micropip(dist, url)
packages[canonicalize_name(name)] = pkg_entry

# Sort
Expand Down

0 comments on commit ea728b6

Please sign in to comment.