From d743e7b92ee283f5774743eb182445a1bc23160f Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 11 Feb 2025 13:35:31 +0000 Subject: [PATCH 01/10] Implement reinstall --- CHANGELOG.md | 8 ++ micropip/install.py | 39 ++++- micropip/package_manager.py | 12 ++ micropip/transaction.py | 38 ++++- micropip/uninstall.py | 133 +++++++++++------- tests/conftest.py | 20 ++- .../wheel/blue-0.9.0-py3-none-any.whl | Bin 0 -> 10579 bytes .../wheel/blue-0.9.1-py3-none-any.whl | Bin 0 -> 10590 bytes tests/test_install.py | 79 +++++++++++ 9 files changed, 263 insertions(+), 66 deletions(-) create mode 100644 tests/test_data/wheel/blue-0.9.0-py3-none-any.whl create mode 100644 tests/test_data/wheel/blue-0.9.1-py3-none-any.whl diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f61c71..06deefa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unrelased + +### Added + +- Added `reinstall` parameter to micropip.install to allow reinstalling + a package that is already installed + [#64](https://github.com/pyodide/micropip/pull/64) + ## [0.9.0] - 2024/02/01 ### Fixed diff --git a/micropip/install.py b/micropip/install.py index 15cda54..66c7136 100644 --- a/micropip/install.py +++ b/micropip/install.py @@ -2,10 +2,11 @@ import importlib from pathlib import Path +from .uninstall import uninstall_distributions from ._compat import loadPackage, to_js from ._vendored.packaging.src.packaging.markers import default_environment from .constants import FAQ_URLS -from .logging import setup_logging +from .logging import indent_log, setup_logging from .transaction import Transaction @@ -18,6 +19,7 @@ async def install( pre: bool = False, *, constraints: list[str] | None = None, + reinstall: bool = False, verbose: bool | int | None = None, ) -> None: with setup_logging().ctx_level(verbose) as logger: @@ -48,6 +50,7 @@ async def install( verbose=verbose, index_urls=index_urls, constraints=constraints, + reinstall=reinstall, ) await transaction.gather_requirements(requirements) @@ -60,7 +63,11 @@ async def install( pyodide_packages, wheels = transaction.pyodide_packages, transaction.wheels - package_names = [pkg.name for pkg in wheels + pyodide_packages] + packages_all = [pkg.name for pkg in wheels + pyodide_packages] + distributions = search_installed_packages(packages_all) + + with indent_log(): + uninstall_distributions(distributions) logger.debug( "Installing packages %r and wheels %r ", @@ -68,8 +75,8 @@ async def install( [w.filename for w in transaction.wheels], ) - if package_names: - logger.info("Installing collected packages: %s", ", ".join(package_names)) + if packages_all: + logger.info("Installing collected packages: %s", ", ".join(packages_all)) # Install PyPI packages # detect whether the wheel metadata is from PyPI or from custom location @@ -90,3 +97,27 @@ async def install( logger.info("Successfully installed %s", ", ".join(packages)) importlib.invalidate_caches() + + +def search_installed_packages(names: list[str]) -> list[importlib.metadata.Distribution]: + """ + Get installed packages by name. + + Parameters + ---------- + names + List of distribution names to search for. + + Returns + ------- + List of distributions that were found. + If a distribution is not found, it is not included in the list. + """ + distributions = [] + for name in names: + try: + distributions.append(importlib.metadata.distribution(name)) + except importlib.metadata.PackageNotFoundError: + pass + + return distributions \ No newline at end of file diff --git a/micropip/package_manager.py b/micropip/package_manager.py index 09ec730..0c0f811 100644 --- a/micropip/package_manager.py +++ b/micropip/package_manager.py @@ -40,6 +40,7 @@ async def install( index_urls: list[str] | str | None = None, *, constraints: list[str] | None = None, + reinstall: bool = False, verbose: bool | int | None = None, ): """Install the given package and all of its dependencies. @@ -132,6 +133,16 @@ async def install( Unlike ``requirements``, the package name _must_ be provided in the PEP-508 format e.g. ``pkgname@https://...``. + reinstall: + + If ``False`` (default), micropip will show an error if the requested package + is already installed, but with a incompatible version. If ``True``, + micropip will uninstall the existing packages that are not compatible with + the requested version and install the packages again. + + Note that packages that are already imported will not be reloaded, so make + sure to reload the module after reinstalling by e.g. running importlib.reload(module). + verbose: Print more information about the process. By default, micropip does not change logger level. Setting ``verbose=True`` will print similar @@ -151,6 +162,7 @@ async def install( credentials, pre, constraints=constraints, + reinstall=reinstall, verbose=verbose, ) diff --git a/micropip/transaction.py b/micropip/transaction.py index 65dca5e..1907368 100644 --- a/micropip/transaction.py +++ b/micropip/transaction.py @@ -44,6 +44,7 @@ class Transaction: verbose: bool | int | None = None constraints: list[str] | None = None + reinstall: bool = False def __post_init__(self) -> None: # If index_urls is None, pyodide-lock.json have to be searched first. @@ -93,7 +94,20 @@ async def add_requirement(self, req: str | Requirement) -> None: return await self.add_requirement_inner(Requirement(req)) - def check_version_satisfied(self, req: Requirement) -> tuple[bool, str]: + def check_version_satisfied(self, req: Requirement, *, allow_reinstall: bool = False) -> tuple[bool, str]: + """ + Check if the installed version of a package satisfies the requirement. + Returns True if the requirement is satisfied, False otherwise. + + Parameters + ---------- + req + The requirement to check. + + allow_reinstall + If False, this function will raise exception if the package is already installed + and the installed version does not satisfy the requirement. + """ ver = None try: ver = importlib.metadata.version(req.name) @@ -109,9 +123,16 @@ def check_version_satisfied(self, req: Requirement) -> tuple[bool, str]: # installed version matches, nothing to do return True, ver - raise ValueError( - f"Requested '{req}', " f"but {req.name}=={ver} is already installed" - ) + if allow_reinstall: + return False, "" + else: + raise ValueError( + f"Requested '{req}', " + f"but {req.name}=={ver} is already installed. " + "If you want to reinstall the package with a different version, " + "use micropip.install(..., reinstall=True) to force reinstall, " + "or micropip.uninstall(...) to uninstall the package first." + ) async def add_requirement_inner( self, @@ -168,7 +189,10 @@ def eval_marker(e: dict[str, str]) -> bool: # Is some version of this package is already installed? req.name = canonicalize_name(req.name) - satisfied, ver = self.check_version_satisfied(req) + satisfied, ver = self.check_version_satisfied( + req, allow_reinstall=self.reinstall + ) + if satisfied: logger.info("Requirement already satisfied: %s (%s)", req, ver) return @@ -240,7 +264,9 @@ async def _add_requirement_from_package_index(self, req: Requirement): # Maybe while we were downloading pypi_json some other branch # installed the wheel? - satisfied, ver = self.check_version_satisfied(req) + satisfied, ver = self.check_version_satisfied( + req, allow_reinstall=self.reinstall + ) if satisfied: logger.info("Requirement already satisfied: %s (%s)", req, ver) diff --git a/micropip/uninstall.py b/micropip/uninstall.py index 1f8d373..f92e094 100644 --- a/micropip/uninstall.py +++ b/micropip/uninstall.py @@ -1,6 +1,8 @@ import importlib import importlib.metadata +import logging from importlib.metadata import Distribution +from collections.abc import Iterable from ._compat import loadedPackages from ._utils import get_files_in_distribution, get_root @@ -21,65 +23,88 @@ def uninstall(packages: str | list[str], *, verbose: bool | int = False) -> None except importlib.metadata.PackageNotFoundError: logger.warning("Skipping '%s' as it is not installed.", package) - for dist in distributions: - # Note: this value needs to be retrieved before removing files, as - # dist.name uses metadata file to get the name - name = dist.name - version = dist.version - - logger.info("Found existing installation: %s %s", name, version) - - root = get_root(dist) - files = get_files_in_distribution(dist) - directories = set() - - for file in files: - if not file.is_file(): - if not file.is_relative_to(root): - # This file is not in the site-packages directory. Probably one of: - # - data_files - # - scripts - # - entry_points - # Since we don't support these, we can ignore them (except for data_files (TODO)) - logger.warning( - "skipping file '%s' that is relative to root", - ) - continue - # see PR 130, it is likely that this is never triggered since Python 3.12 - # as non existing files are not listed by get_files_in_distribution anymore. - logger.warning( - "A file '%s' listed in the metadata of '%s' does not exist.", - file, - name, - ) + uninstall_distributions(distributions, logger) - continue - - file.unlink() + importlib.invalidate_caches() - if file.parent != root: - directories.add(file.parent) - # Remove directories in reverse hierarchical order - for directory in sorted( - directories, key=lambda x: len(x.parts), reverse=True - ): - try: - directory.rmdir() - except OSError: +def uninstall_distributions( + distributions: Iterable[Distribution], + logger: logging.Logger, # TODO: move this to an attribute of the PackageManager +) -> None: + """ + Uninstall the given package distributions. + + This function does not do any checks, so make sure that the distributions + are installed and that they are installed using a wheel file, i.e. packages + that have distribution metadata. + + This function also does not invalidate the import cache, so make sure to + call `importlib.invalidate_caches()` after calling this function. + + Parameters + ---------- + distributions + Package distributions to uninstall. + + """ + for dist in distributions: + # Note: this value needs to be retrieved before removing files, as + # dist.name uses metadata file to get the name + name = dist.name + version = dist.version + + logger.info("Found existing installation: %s %s", name, version) + + root = get_root(dist) + files = get_files_in_distribution(dist) + directories = set() + + for file in files: + if not file.is_file(): + if not file.is_relative_to(root): + # This file is not in the site-packages directory. Probably one of: + # - data_files + # - scripts + # - entry_points + # Since we don't support these, we can ignore them (except for data_files (TODO)) logger.warning( - "A directory '%s' is not empty after uninstallation of '%s'. " - "This might cause problems when installing a new version of the package. ", - directory, - name, + "skipping file '%s' that is relative to root", ) + continue + # see PR 130, it is likely that this is never triggered since Python 3.12 + # as non existing files are not listed by get_files_in_distribution anymore. + logger.warning( + "A file '%s' listed in the metadata of '%s' does not exist.", + file, + name, + ) - if hasattr(loadedPackages, name): - delattr(loadedPackages, name) - else: - # This should not happen, but just in case - logger.warning("a package '%s' was not found in loadedPackages.", name) + continue - logger.info("Successfully uninstalled %s-%s", name, version) + file.unlink() - importlib.invalidate_caches() + if file.parent != root: + directories.add(file.parent) + + # Remove directories in reverse hierarchical order + for directory in sorted( + directories, key=lambda x: len(x.parts), reverse=True + ): + try: + directory.rmdir() + except OSError: + logger.warning( + "A directory '%s' is not empty after uninstallation of '%s'. " + "This might cause problems when installing a new version of the package. ", + directory, + name, + ) + + if hasattr(loadedPackages, name): + delattr(loadedPackages, name) + else: + # This should not happen, but just in case + logger.warning("a package '%s' was not found in loadedPackages.", name) + + logger.info("Successfully uninstalled %s-%s", name, version) \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index dbbe3c7..466f120 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -153,11 +153,17 @@ def add_wheel(self, path: Path, replace: bool = True): if name in self._wheels and not replace: return + # Access the wheel by name or by (name, version) + version_str = str(version) self._wheels[name] = self.Wheel( - path, name, str(version), path.name, name.replace("-", "_"), url + path, name, version_str, path.name, name.replace("-", "_"), url ) + self._wheels[(name, version_str)] = self._wheels[name] + + def get(self, name: str, version: str | None = None) -> Wheel: + if version is not None: + return self._wheels[(name, version)] - def get(self, name: str) -> Wheel: return self._wheels[name] @@ -220,10 +226,20 @@ def _mock_importlib_version(name: str) -> str: def _mock_importlib_distributions(): return (Distribution.at(p) for p in wheel_base.glob("*.dist-info")) # type: ignore[union-attr] + def _mock_importlib_distribution(name: str) -> Distribution: + for dist in _mock_importlib_distributions(): + if dist.name == name: + return dist + + raise PackageNotFoundError(name) + monkeypatch.setattr(importlib.metadata, "version", _mock_importlib_version) monkeypatch.setattr( importlib.metadata, "distributions", _mock_importlib_distributions ) + monkeypatch.setattr( + importlib.metadata, "distribution", _mock_importlib_distribution + ) monkeypatch.setattr( importlib.metadata.Distribution, "from_name", _mock_importlib_from_name ) diff --git a/tests/test_data/wheel/blue-0.9.0-py3-none-any.whl b/tests/test_data/wheel/blue-0.9.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..643fb230ac8618217bb9e8aae05d3154edc831e1 GIT binary patch literal 10579 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 literal 0 HcmV?d00001 diff --git a/tests/test_data/wheel/blue-0.9.1-py3-none-any.whl b/tests/test_data/wheel/blue-0.9.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..c3569b35688626e6852c24e6e14b232c15e0fc18 GIT binary patch literal 10590 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 literal 0 HcmV?d00001 diff --git a/tests/test_install.py b/tests/test_install.py index 21ece30..9f6e675 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -434,3 +434,82 @@ async def run(selenium, numpy_url, shapely_url): Point(0, 0) run(selenium, numpy_wheel.url, shapely_wheel.url) + + +@pytest.mark.asyncio +async def test_reinstall_different_version( + mock_fetch: mock_fetch_cls, + mock_importlib, +) -> None: + import importlib.metadata + + import pytest + + dummy = "dummy" + version_old = "1.0.0" + version_new = "2.0.0" + + mock_fetch.add_pkg_version(dummy, version_old) + mock_fetch.add_pkg_version(dummy, version_new) + + await micropip.install(f"{dummy}=={version_new}") + assert micropip.list()[dummy].version == version_new + assert importlib.metadata.version(dummy) == version_new + + with pytest.raises(ValueError, match="already installed"): + await micropip.install(f"{dummy}=={version_old}", reinstall=False) + + await micropip.install(f"{dummy}=={version_old}", reinstall=True) + assert micropip.list()[dummy].version == version_old + assert importlib.metadata.version(dummy) == version_old + + +def test_reinstall_different_version2(selenium_standalone_micropip, wheel_catalog): + selenium = selenium_standalone_micropip + blue_wheel1 = wheel_catalog.get(("blue", "0.9.0")) + blue_wheel2 = wheel_catalog.get(("blue", "0.9.1")) + + @run_in_pyodide + async def run(selenium, v1, v2): + import pytest + + import micropip + + micropip.install(v1) + with pytest.raises(ValueError, match="already installed"): + micropip.install(v2, reinstall=False) + + micropip.install(v2, reinstall=True) + + run(selenium, blue_wheel1.url, blue_wheel2.url) + + +def test_reinstall_reload(selenium_standalone_micropip, wheel_catalog): + selenium = selenium_standalone_micropip + blue_wheel1 = wheel_catalog.get(("blue", "0.9.0")) + blue_wheel2 = wheel_catalog.get(("blue", "0.9.1")) + + @run_in_pyodide + async def run(selenium, v1, v2): + import micropip + + micropip.install(v1) + + import blue + + assert blue.__version__ == "0.9.0" + + micropip.install(v2, reinstall=True) + + import blue + + # still the old version + assert blue.__version__ == "0.9.0" + + from importlib import reload + + reload(blue) + + assert blue.__version__ == "0.9.1" + + run(selenium, blue_wheel1.url, blue_wheel2.url) \ No newline at end of file From b2f35f3bb1204f8018c6f5cf442e1ee1cff501ba Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 11 Feb 2025 13:45:45 +0000 Subject: [PATCH 02/10] Use different wheel --- .../wheel/blue-0.9.0-py3-none-any.whl | Bin 10579 -> 0 bytes .../wheel/blue-0.9.1-py3-none-any.whl | Bin 10590 -> 0 bytes .../wheel/mccabe-0.6.1-py2.py3-none-any.whl | Bin 0 -> 8556 bytes .../wheel/mccabe-0.7.0-py2.py3-none-any.whl | Bin 0 -> 7350 bytes tests/test_install.py | 24 +++++++++--------- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 tests/test_data/wheel/blue-0.9.0-py3-none-any.whl delete mode 100644 tests/test_data/wheel/blue-0.9.1-py3-none-any.whl create mode 100644 tests/test_data/wheel/mccabe-0.6.1-py2.py3-none-any.whl create mode 100644 tests/test_data/wheel/mccabe-0.7.0-py2.py3-none-any.whl diff --git a/tests/test_data/wheel/blue-0.9.0-py3-none-any.whl b/tests/test_data/wheel/blue-0.9.0-py3-none-any.whl deleted file mode 100644 index 643fb230ac8618217bb9e8aae05d3154edc831e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10579 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 diff --git a/tests/test_data/wheel/blue-0.9.1-py3-none-any.whl b/tests/test_data/wheel/blue-0.9.1-py3-none-any.whl deleted file mode 100644 index c3569b35688626e6852c24e6e14b232c15e0fc18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10590 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 diff --git a/tests/test_data/wheel/mccabe-0.6.1-py2.py3-none-any.whl b/tests/test_data/wheel/mccabe-0.6.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..2ffd0421b15d4521e624e31c03f678a43da71e43 GIT binary patch literal 8556 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 VKtTNGbL8K*)$hgPjQpRc{{`2;ryKwP literal 0 HcmV?d00001 diff --git a/tests/test_data/wheel/mccabe-0.7.0-py2.py3-none-any.whl b/tests/test_data/wheel/mccabe-0.7.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..0c6a72ceecf8311fdfd985d03f1fb8cf1f22c92e GIT binary patch literal 7350 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 G+y4MN(OE12 literal 0 HcmV?d00001 diff --git a/tests/test_install.py b/tests/test_install.py index 9f6e675..3b88c21 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -466,8 +466,8 @@ async def test_reinstall_different_version( def test_reinstall_different_version2(selenium_standalone_micropip, wheel_catalog): selenium = selenium_standalone_micropip - blue_wheel1 = wheel_catalog.get(("blue", "0.9.0")) - blue_wheel2 = wheel_catalog.get(("blue", "0.9.1")) + mccabe_wheel1 = wheel_catalog.get(("mccabe", "0.6.1")) + mccabe_wheel2 = wheel_catalog.get(("mccabe", "0.7.0")) @run_in_pyodide async def run(selenium, v1, v2): @@ -481,13 +481,13 @@ async def run(selenium, v1, v2): micropip.install(v2, reinstall=True) - run(selenium, blue_wheel1.url, blue_wheel2.url) + run(selenium, mccabe_wheel1.url, mccabe_wheel2.url) def test_reinstall_reload(selenium_standalone_micropip, wheel_catalog): selenium = selenium_standalone_micropip - blue_wheel1 = wheel_catalog.get(("blue", "0.9.0")) - blue_wheel2 = wheel_catalog.get(("blue", "0.9.1")) + mccabe_wheel1 = wheel_catalog.get(("mccabe", "0.6.1")) + mccabe_wheel2 = wheel_catalog.get(("mccabe", "0.7.0")) @run_in_pyodide async def run(selenium, v1, v2): @@ -495,21 +495,21 @@ async def run(selenium, v1, v2): micropip.install(v1) - import blue + import mccabe - assert blue.__version__ == "0.9.0" + assert mccabe.__version__ == "0.6.1" micropip.install(v2, reinstall=True) - import blue + import mccabe # still the old version - assert blue.__version__ == "0.9.0" + assert mccabe.__version__ == "0.6.1" from importlib import reload - reload(blue) + reload(mccabe) - assert blue.__version__ == "0.9.1" + assert mccabe.__version__ == "0.7.0" - run(selenium, blue_wheel1.url, blue_wheel2.url) \ No newline at end of file + run(selenium, mccabe_wheel1.url, mccabe_wheel2.url) \ No newline at end of file From bdc71663d8ae5c5257627fb8dbd95cd98f03bc37 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 11 Feb 2025 13:46:22 +0000 Subject: [PATCH 03/10] Fix param --- micropip/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropip/install.py b/micropip/install.py index 66c7136..b711633 100644 --- a/micropip/install.py +++ b/micropip/install.py @@ -67,7 +67,7 @@ async def install( distributions = search_installed_packages(packages_all) with indent_log(): - uninstall_distributions(distributions) + uninstall_distributions(distributions, logger) logger.debug( "Installing packages %r and wheels %r ", From 40bd3e5c0093202e8a097e4fa8d526b7e0930a6d Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 11 Feb 2025 14:11:05 +0000 Subject: [PATCH 04/10] Use integration test [integration] --- tests/conftest.py | 20 +------ tests/integration/test_integration.py | 28 ++++++++++ .../wheel/mccabe-0.6.1-py2.py3-none-any.whl | Bin 8556 -> 0 bytes .../wheel/mccabe-0.7.0-py2.py3-none-any.whl | Bin 7350 -> 0 bytes tests/test_install.py | 51 ------------------ 5 files changed, 30 insertions(+), 69 deletions(-) delete mode 100644 tests/test_data/wheel/mccabe-0.6.1-py2.py3-none-any.whl delete mode 100644 tests/test_data/wheel/mccabe-0.7.0-py2.py3-none-any.whl diff --git a/tests/conftest.py b/tests/conftest.py index 466f120..dbbe3c7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -153,17 +153,11 @@ def add_wheel(self, path: Path, replace: bool = True): if name in self._wheels and not replace: return - # Access the wheel by name or by (name, version) - version_str = str(version) self._wheels[name] = self.Wheel( - path, name, version_str, path.name, name.replace("-", "_"), url + path, name, str(version), path.name, name.replace("-", "_"), url ) - self._wheels[(name, version_str)] = self._wheels[name] - - def get(self, name: str, version: str | None = None) -> Wheel: - if version is not None: - return self._wheels[(name, version)] + def get(self, name: str) -> Wheel: return self._wheels[name] @@ -226,20 +220,10 @@ def _mock_importlib_version(name: str) -> str: def _mock_importlib_distributions(): return (Distribution.at(p) for p in wheel_base.glob("*.dist-info")) # type: ignore[union-attr] - def _mock_importlib_distribution(name: str) -> Distribution: - for dist in _mock_importlib_distributions(): - if dist.name == name: - return dist - - raise PackageNotFoundError(name) - monkeypatch.setattr(importlib.metadata, "version", _mock_importlib_version) monkeypatch.setattr( importlib.metadata, "distributions", _mock_importlib_distributions ) - monkeypatch.setattr( - importlib.metadata, "distribution", _mock_importlib_distribution - ) monkeypatch.setattr( importlib.metadata.Distribution, "from_name", _mock_importlib_from_name ) diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index d50a8af..5906816 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -52,6 +52,34 @@ async def _run(selenium): _run(selenium_standalone_micropip) +@integration_test_only +def test_integration_install_reinstall(selenium_standalone_micropip, pytestconfig): + @run_in_pyodide + async def _run(selenium): + import micropip + + await micropip.install("mccabe==0.6.1") + + import mccabe + + assert mccabe.__version__ == "0.6.1" + + try: + await micropip.install("mccabe==0.7.0", reinstall=False) + except ValueError as e: + assert "already installed" in str(e) + else: + raise Exception("Should raise!") + + await micropip.install("mccabe==0.7.0", reinstall=True) + + import mccabe + + assert mccabe.__version__ == "0.7.0" + + _run(selenium_standalone_micropip) + + @integration_test_only def test_integration_list_basic(selenium_standalone_micropip, pytestconfig): @run_in_pyodide diff --git a/tests/test_data/wheel/mccabe-0.6.1-py2.py3-none-any.whl b/tests/test_data/wheel/mccabe-0.6.1-py2.py3-none-any.whl deleted file mode 100644 index 2ffd0421b15d4521e624e31c03f678a43da71e43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8556 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 VKtTNGbL8K*)$hgPjQpRc{{`2;ryKwP diff --git a/tests/test_data/wheel/mccabe-0.7.0-py2.py3-none-any.whl b/tests/test_data/wheel/mccabe-0.7.0-py2.py3-none-any.whl deleted file mode 100644 index 0c6a72ceecf8311fdfd985d03f1fb8cf1f22c92e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7350 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 G+y4MN(OE12 diff --git a/tests/test_install.py b/tests/test_install.py index 3b88c21..3ee1aa4 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -462,54 +462,3 @@ async def test_reinstall_different_version( await micropip.install(f"{dummy}=={version_old}", reinstall=True) assert micropip.list()[dummy].version == version_old assert importlib.metadata.version(dummy) == version_old - - -def test_reinstall_different_version2(selenium_standalone_micropip, wheel_catalog): - selenium = selenium_standalone_micropip - mccabe_wheel1 = wheel_catalog.get(("mccabe", "0.6.1")) - mccabe_wheel2 = wheel_catalog.get(("mccabe", "0.7.0")) - - @run_in_pyodide - async def run(selenium, v1, v2): - import pytest - - import micropip - - micropip.install(v1) - with pytest.raises(ValueError, match="already installed"): - micropip.install(v2, reinstall=False) - - micropip.install(v2, reinstall=True) - - run(selenium, mccabe_wheel1.url, mccabe_wheel2.url) - - -def test_reinstall_reload(selenium_standalone_micropip, wheel_catalog): - selenium = selenium_standalone_micropip - mccabe_wheel1 = wheel_catalog.get(("mccabe", "0.6.1")) - mccabe_wheel2 = wheel_catalog.get(("mccabe", "0.7.0")) - - @run_in_pyodide - async def run(selenium, v1, v2): - import micropip - - micropip.install(v1) - - import mccabe - - assert mccabe.__version__ == "0.6.1" - - micropip.install(v2, reinstall=True) - - import mccabe - - # still the old version - assert mccabe.__version__ == "0.6.1" - - from importlib import reload - - reload(mccabe) - - assert mccabe.__version__ == "0.7.0" - - run(selenium, mccabe_wheel1.url, mccabe_wheel2.url) \ No newline at end of file From 88e7dbccdbb45a7d1d25f8696a68e7103f1af452 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 11 Feb 2025 14:17:22 +0000 Subject: [PATCH 05/10] Fix test [integration] --- tests/integration/test_integration.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 5906816..8d9661e 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -75,6 +75,13 @@ async def _run(selenium): import mccabe + # still 0.6.1 + assert mccabe.__version__ == "0.6.1" + + import importlib + + importlib.reload(mccabe) + assert mccabe.__version__ == "0.7.0" _run(selenium_standalone_micropip) From 380af1c391f0ef7d67a656f23d3dd7551803af45 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:31:13 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- micropip/install.py | 8 +++++--- micropip/transaction.py | 4 +++- micropip/uninstall.py | 8 +++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/micropip/install.py b/micropip/install.py index b711633..b2927c9 100644 --- a/micropip/install.py +++ b/micropip/install.py @@ -2,12 +2,12 @@ import importlib from pathlib import Path -from .uninstall import uninstall_distributions from ._compat import loadPackage, to_js from ._vendored.packaging.src.packaging.markers import default_environment from .constants import FAQ_URLS from .logging import indent_log, setup_logging from .transaction import Transaction +from .uninstall import uninstall_distributions async def install( @@ -99,7 +99,9 @@ async def install( importlib.invalidate_caches() -def search_installed_packages(names: list[str]) -> list[importlib.metadata.Distribution]: +def search_installed_packages( + names: list[str], +) -> list[importlib.metadata.Distribution]: """ Get installed packages by name. @@ -120,4 +122,4 @@ def search_installed_packages(names: list[str]) -> list[importlib.metadata.Distr except importlib.metadata.PackageNotFoundError: pass - return distributions \ No newline at end of file + return distributions diff --git a/micropip/transaction.py b/micropip/transaction.py index 1907368..a3337bf 100644 --- a/micropip/transaction.py +++ b/micropip/transaction.py @@ -94,7 +94,9 @@ async def add_requirement(self, req: str | Requirement) -> None: return await self.add_requirement_inner(Requirement(req)) - def check_version_satisfied(self, req: Requirement, *, allow_reinstall: bool = False) -> tuple[bool, str]: + def check_version_satisfied( + self, req: Requirement, *, allow_reinstall: bool = False + ) -> tuple[bool, str]: """ Check if the installed version of a package satisfies the requirement. Returns True if the requirement is satisfied, False otherwise. diff --git a/micropip/uninstall.py b/micropip/uninstall.py index f92e094..3ebc8e3 100644 --- a/micropip/uninstall.py +++ b/micropip/uninstall.py @@ -1,8 +1,8 @@ import importlib import importlib.metadata import logging -from importlib.metadata import Distribution from collections.abc import Iterable +from importlib.metadata import Distribution from ._compat import loadedPackages from ._utils import get_files_in_distribution, get_root @@ -88,9 +88,7 @@ def uninstall_distributions( directories.add(file.parent) # Remove directories in reverse hierarchical order - for directory in sorted( - directories, key=lambda x: len(x.parts), reverse=True - ): + for directory in sorted(directories, key=lambda x: len(x.parts), reverse=True): try: directory.rmdir() except OSError: @@ -107,4 +105,4 @@ def uninstall_distributions( # This should not happen, but just in case logger.warning("a package '%s' was not found in loadedPackages.", name) - logger.info("Successfully uninstalled %s-%s", name, version) \ No newline at end of file + logger.info("Successfully uninstalled %s-%s", name, version) From 29eecefc6b1681cb0768118e43299baaf0048584 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Thu, 20 Feb 2025 12:53:51 +0000 Subject: [PATCH 07/10] Add reinstall check --- micropip/package_manager.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/micropip/package_manager.py b/micropip/package_manager.py index 7797aed..fd4b44b 100644 --- a/micropip/package_manager.py +++ b/micropip/package_manager.py @@ -209,11 +209,14 @@ async def install( packages_all = [pkg.name for pkg in wheels + pyodide_packages] - # This will return non-empty list only when reinstall is True, - # When reinstall is False, the transaction will fail if there is an installed package + distributions = search_installed_packages(packages_all) - with indent_log(): - self._uninstall_distributions(distributions, logger) + # This check is redundant because the distributions will always be an emtpy list when reinstall==False + # (no installed packages will be returned from transaction) + # But just in case. + if reinstall: + with indent_log(): + self._uninstall_distributions(distributions, logger) logger.debug( "Installing packages %r and wheels %r ", From c6e84d87ecd52ebdb2777ccf6160e0adf7a0ae06 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:54:18 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- micropip/package_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/micropip/package_manager.py b/micropip/package_manager.py index fd4b44b..56bdab7 100644 --- a/micropip/package_manager.py +++ b/micropip/package_manager.py @@ -209,7 +209,6 @@ async def install( packages_all = [pkg.name for pkg in wheels + pyodide_packages] - distributions = search_installed_packages(packages_all) # This check is redundant because the distributions will always be an emtpy list when reinstall==False # (no installed packages will be returned from transaction) From 265778ca41d1456cfe738ec7c7c570a35fd24692 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 09:33:11 +0000 Subject: [PATCH 09/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/integration/test_integration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 8abae5c..12a5d55 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -83,10 +83,10 @@ async def _run(selenium): importlib.reload(mccabe) assert mccabe.__version__ == "0.7.0" - + _run(selenium_standalone_micropip) - + @integration_test_only def test_integration_install_yanked(selenium_standalone_micropip, pytestconfig): @run_in_pyodide From 1e26d75b0bd7a0dc50d9cb0ae48039d03104d47a Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Sun, 16 Mar 2025 17:36:13 +0900 Subject: [PATCH 10/10] typo --- micropip/package_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropip/package_manager.py b/micropip/package_manager.py index 56bdab7..3a433f3 100644 --- a/micropip/package_manager.py +++ b/micropip/package_manager.py @@ -210,7 +210,7 @@ async def install( packages_all = [pkg.name for pkg in wheels + pyodide_packages] distributions = search_installed_packages(packages_all) - # This check is redundant because the distributions will always be an emtpy list when reinstall==False + # This check is redundant because the distributions will always be an empty list when reinstall==False # (no installed packages will be returned from transaction) # But just in case. if reinstall: